C++11中加入了<thread>头文件,此头文件主要声明了std::thread线程类。C++11的标准类std::thread对线程进行了封装。std::thread代表了一个线程对象。应用C++11中的std::thread便于多线程程序的移值。

<thread>是C++标准程序库中的一个头文件,定义了C++11标准中的一些表示线程的类、用于互斥访问的类与方法等。

类std::thread表示一个线程。初始化时给出该线程的执行函数(或是可以调用的对象)。线程对象构造后即开始运行。默认情况下,C++11的子线程必须与主线程会合,即在主线程中调用thread::join()函数,这避免了子线程还在执行,主线程已经执行结束而撤销的情况。

如果子线程的执行函数需要参数,可把实参列表写在std::thread对象构造函数的参数列表中。如果把可调用对象(callable object)作为参数传给子线程的构造函数,则把该可调用对象复制一份给子线程。如果需要传递可调用对象的左值引用给子线程,则采用std::ref()来产生对象的引用、然后把引用值再传进去给子线程。

C++11所定义的线程是和操作系统的线程一一对应的,也就是说我们生成的线程都是直接接受操作系统的调度的,一个进程所能创建的线程数目以及一个操作系统所能创建的总的线程数据等都由运行时操作系统限定。

std::thread中主要声明三类函数:(1)、构造函数、拷贝构造函数(拷贝构造函数被禁用,意味着thread不可被拷贝构造,但能被转移(move)或者互换(swap))及析构函数;(2)、成员函数;(3)、静态成员函数(hardware_concurrency,检测硬件并发特性, Returns the number of hardware thread contexts)。

std::thread类成员函数:

(1)、get_id:获取线程ID,返回一个类型为std::thread::id的对象。

(2)、joinable:检查线程是否可被join。检查thread对象是否标识一个活动(active)的可行性线程。缺省构造的thread对象、已经完成join的thread对象、已经detach的thread对象都不是joinable。

(3)、join:调用该函数会阻塞当前线程。阻塞调用者(caller)所在的线程直至被join的std::thread对象标识的线程执行结束。

(4)、detach:将当前线程对象所代表的执行实例与该线程对象分离,使得线程的执行可以单独进行。一旦线程执行完毕,它所分配的资源将会被释放。

(5)、native_handle:该函数返回与std::thread具体实现相关的线程句柄。native_handle_type是连接thread类和操作系统SDK API之间的桥梁,如在Linux g++(libstdc++)里,native_handle_type其实就是pthread里面的pthread_t类型,当thread类的功能不能满足我们的要求的时候(比如改变某个线程的优先级),可以通过thread类实例的native_handle()返回值作为参数来调用相关的pthread函数达到目录。This member function is only present in class thread if the library implementation supports it. If present, it returns a value used to access implementation-specific information associated to the thread.

(6)、swap:交换两个线程对象所代表的底层句柄。

(7)、operator=:moves the thread object

(8)、hardware_concurrency:静态成员函数,返回当前计算机最大的硬件并发线程数目。基本上可以视为处理器的核心数目。

另外,std::thread::id表示线程ID,定义了在运行时操作系统内唯一能够标识该线程的标识符,同时其值还能指示所标识的线程的状态。Values of this type are returned by thread::get_id and this_thread::get_id to identify threads.

有时候我们需要在线程执行代码里面对当前调用者线程进行操作,针对这种情况,C++11里面专门定义了一个命名空间this_thread,此命名空间也声明在<thread>头文件中,其中包括get_id()函数用来获取当前调用者线程的ID;yield()函数可以用来将调用者线程跳出运行状态,重新交给操作系统进行调度,即当前线程放弃执行,操作系统调度另一线程继续执行;sleep_until()函数是将线程休眠至某个指定的时刻(time point),该线程才被重新唤醒;sleep_for()函数是将线程休眠某个指定的时间片(time span),该线程才被重新唤醒,不过由于线程调度等原因,实际休眠实际可能比sleep_duration所表示的时间片更长。

std::thread:Class to represent individual threads of execution. A thread of execution is a sequence of instructions that can be executed concurrently with other such sequences in multithreading environments, while sharing a same address space.

std::this_thread:This namespace groups a set of functions that access the current thread.

下面是从其他文章中copy的<thread>测试代码,详细内容介绍可以参考对应的reference:

  1. #include "thread2.hpp"
  2. #include <iostream>
  3. #include <vector>
  4. #include <functional>
  5. #include <memory>
  6. #include <list>
  7. #include <mutex>
  8. #include <condition_variable>
  9. #include <atomic>
  10. #include <thread>
  11. #include <chrono>
  12. #include <iomanip>
  13. #include <ctime>
  14. #include <algorithm>
  15. namespace thread_ {
  16. std::atomic<int> global_counter(0);
  17. #ifdef _MSC_VER
  18. ///
  19. // reference: http://www.cplusplus.com/reference/thread/thread/thread/
  20. static void increase_global(int n) { for (int i = 0; i<n; ++i) ++global_counter; }
  21. static void increase_reference(std::atomic<int>& variable, int n) { for (int i = 0; i<n; ++i) ++variable; }
  22. struct C : std::atomic<int> {
  23. C() : std::atomic<int>(0) {}
  24. void increase_member(int n) { for (int i = 0; i<n; ++i) fetch_add(1); }
  25. };
  26. int test_thread_thread()
  27. {
  28. // thread::thread: Constructs a thread object
  29. std::vector<std::thread> threads;
  30. std::cout << "increase global counter with 10 threads...\n";
  31. for (int i = 1; i <= 10; ++i)
  32. threads.push_back(std::thread(increase_global, 1000));
  33. std::cout << "increase counter (foo) with 10 threads using reference...\n";
  34. std::atomic<int> foo(0);
  35. for (int i = 1; i <= 10; ++i)
  36. threads.push_back(std::thread(increase_reference, std::ref(foo), 1000));
  37. std::cout << "increase counter (bar) with 10 threads using member...\n";
  38. C bar;
  39. for (int i = 1; i <= 10; ++i)
  40. threads.push_back(std::thread(&C::increase_member, std::ref(bar), 1000));
  41. std::cout << "synchronizing all threads...\n";
  42. for (auto& th : threads) th.join();
  43. std::cout << "global_counter: " << global_counter << '\n';
  44. std::cout << "foo: " << foo << '\n';
  45. std::cout << "bar: " << bar << '\n';
  46. return 0;
  47. }
  48. // reference: http://www.cplusplus.com/reference/thread/thread/detach/
  49. static void pause_thread(int n)
  50. {
  51. std::this_thread::sleep_for(std::chrono::seconds(n));
  52. std::cout << "pause of " << n << " seconds ended\n";
  53. }
  54. int test_thread_detach()
  55. {
  56. // thread::detach: Detaches the thread represented by the object from the calling thread,
  57. // allowing them to execute independently from each other.
  58. std::cout << "Spawning and detaching 3 threads...\n";
  59. std::thread(pause_thread, 1).detach();
  60. std::thread(pause_thread, 2).detach();
  61. std::thread(pause_thread, 3).detach();
  62. std::cout << "Done spawning threads.\n";
  63. std::cout << "(the main thread will now pause for 5 seconds)\n";
  64. // give the detached threads time to finish (but not guaranteed!):
  65. pause_thread(5);
  66. return 0;
  67. }
  68. //
  69. // reference: http://www.cplusplus.com/reference/thread/thread/get_id/
  70. std::thread::id main_thread_id = std::this_thread::get_id();
  71. static void is_main_thread()
  72. {
  73. // this_thread::get_id: Returns the thread id of the calling thread.
  74. // This value uniquely identifies the thread.
  75. if (main_thread_id == std::this_thread::get_id())
  76. std::cout << "This is the main thread.\n";
  77. else
  78. std::cout << "This is not the main thread.\n";
  79. }
  80. int test_thread_get_id()
  81. {
  82. // thread::get_id: Returns the thread id
  83. // If the thread object is joinable, the function returns a value that uniquely identifies the thread.
  84. // If the thread object is not joinable, the function returns a default - constructed object of member type thread::id.
  85. is_main_thread();
  86. std::thread th(is_main_thread);
  87. th.join();
  88. return 0;
  89. }
  90. /
  91. // reference: http://www.cplusplus.com/reference/thread/thread/join/
  92. int test_thread_join()
  93. {
  94. // thread::join: The function returns when the thread execution has completed.
  95. std::cout << "Spawning 3 threads...\n";
  96. std::thread t1(pause_thread, 1);
  97. std::thread t2(pause_thread, 2);
  98. std::thread t3(pause_thread, 3);
  99. std::cout << "Done spawning threads. Now waiting for them to join:\n";
  100. t1.join();
  101. t2.join();
  102. t3.join();
  103. std::cout << "All threads joined!\n";
  104. return 0;
  105. }
  106. ///
  107. // reference: http://www.cplusplus.com/reference/thread/thread/joinable/
  108. static void mythread()
  109. {
  110. // do stuff...
  111. }
  112. int test_thread_joinable()
  113. {
  114. // thread::joinable: Returns whether the thread object is joinable.
  115. // A thread object is joinable if it represents a thread of execution.
  116. std::thread foo; // 缺省构造函数,线程不可执行
  117. std::thread bar(mythread);
  118. std::cout << "Joinable after construction:\n" << std::boolalpha;
  119. std::cout << "foo: " << foo.joinable() << '\n';
  120. std::cout << "bar: " << bar.joinable() << '\n';
  121. if (foo.joinable()) foo.join();
  122. if (bar.joinable()) bar.join();
  123. std::cout << "Joinable after joining:\n" << std::boolalpha;
  124. std::cout << "foo: " << foo.joinable() << '\n';
  125. std::cout << "bar: " << bar.joinable() << '\n';
  126. return 0;
  127. }
  128. //
  129. // reference: http://www.cplusplus.com/reference/thread/thread/operator=/
  130. int test_thread_operator()
  131. {
  132. // thread::operator=: Move-assign thread
  133. // If the object is currently not joinable, it acquires the thread of execution represented by rhs(if any).
  134. // If it is joinable, terminate() is called.
  135. // After the call, rhs no longer represents any thread of execution(as if default - constructed).
  136. std::thread threads[5]; // default-constructed threads
  137. std::cout << "Spawning 5 threads...\n";
  138. for (int i = 0; i<5; ++i)
  139. threads[i] = std::thread(pause_thread, i + 1); // move-assign threads
  140. std::cout << "Done spawning threads. Now waiting for them to join:\n";
  141. for (int i = 0; i<5; ++i)
  142. threads[i].join();
  143. std::cout << "All threads joined!\n";
  144. return 0;
  145. }
  146. //
  147. // reference: http://www.cplusplus.com/reference/thread/this_thread/sleep_for/
  148. int test_this_thread_sleep_for()
  149. {
  150. // this_thread::sleep_for: Blocks execution of the calling thread during the span of time specified by rel_time.
  151. // The execution of the current thread is stopped until at least rel_time has passed from now.
  152. // Other threads continue their execution.
  153. std::cout << "countdown:\n";
  154. for (int i = 10; i>0; --i) {
  155. std::cout << i << std::endl;
  156. std::this_thread::sleep_for(std::chrono::seconds(1));
  157. }
  158. std::cout << "Lift off!\n";
  159. return 0;
  160. }
  161. /
  162. // reference: http://www.cplusplus.com/reference/thread/this_thread/sleep_until/
  163. int test_this_thread_sleep_until()
  164. {
  165. // this_thread::sleep_until: Blocks the calling thread until abs_time.
  166. // The execution of the current thread is stopped until at least abs_time, while other threads may continue to advance.
  167. using std::chrono::system_clock;
  168. std::time_t tt = system_clock::to_time_t(system_clock::now());
  169. struct std::tm * ptm = std::localtime(&tt);
  170. #ifdef _MSC_VER
  171. std::cout << "Current time: " << std::put_time(ptm, "%X") << '\n';
  172. #endif
  173. std::cout << "Waiting for the next minute to begin...\n";
  174. ++ptm->tm_min; ptm->tm_sec = 0;
  175. std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm)));
  176. #ifdef _MSC_VER
  177. std::cout << std::put_time(ptm, "%X") << " reached!\n";
  178. #endif
  179. return 0;
  180. }
  181. /
  182. // reference: http://www.cplusplus.com/reference/thread/this_thread/yield/
  183. std::atomic<bool> ready(false);
  184. static void count1m(int id)
  185. {
  186. while (!ready) { // wait until main() sets ready...
  187. std::this_thread::yield();
  188. }
  189. for (volatile int i = 0; i<1000000; ++i) {}
  190. std::cout << id << std::endl;
  191. }
  192. int test_this_thread_yield()
  193. {
  194. // this_thread::yield: The calling thread yields, offering the implementation the opportunity to reschedule.
  195. // This function shall be called when a thread waits for other threads to advance without blocking.
  196. std::thread threads[10];
  197. std::cout << "race of 10 threads that count to 1 million:\n";
  198. for (int i = 0; i<10; ++i) threads[i] = std::thread(count1m, i);
  199. ready = true; // go!
  200. for (auto& th : threads) th.join();
  201. std::cout << '\n';
  202. return 0;
  203. }
  204. //
  205. // reference: https://zh.wikibooks.org/zh-hans/C%2B%2B/STL/Thread
  206. template<typename T>
  207. class SyncQueue {
  208. public:
  209. SyncQueue(int maxSize) :m_maxSize(maxSize), m_needStop(false) {}
  210. void Put(const T&x) { Add(x); }
  211. void Put(T&&x) { Add(std::forward<T>(x)); }
  212. void Take(std::list<T>& list)
  213. {
  214. std::unique_lock<std::mutex> locker(m_mutex);
  215. m_notEmpty.wait(locker, [this] {return m_needStop || NotEmpty(); });
  216. if (m_needStop) return;
  217. list = std::move(m_queue);
  218. m_notFull.notify_one();
  219. }
  220. void Take(T& t)
  221. {
  222. std::unique_lock<std::mutex> locker(m_mutex);
  223. m_notEmpty.wait(locker, [this] {return m_needStop || NotEmpty(); });
  224. if (m_needStop) return;
  225. t = m_queue.front();
  226. m_queue.pop_front();
  227. m_notFull.notify_one();
  228. }
  229. void Stop()
  230. {
  231. {
  232. std::lock_guard<std::mutex> locker(m_mutex);
  233. m_needStop = true;
  234. }
  235. m_notFull.notify_all();
  236. m_notEmpty.notify_all();
  237. }
  238. bool Empty()
  239. {
  240. std::lock_guard<std::mutex> locker(m_mutex);
  241. return m_queue.empty();
  242. }
  243. bool Full()
  244. {
  245. std::lock_guard<std::mutex> locker(m_mutex);
  246. return m_queue.size() == m_maxSize;
  247. }
  248. size_t Size()
  249. {
  250. std::lock_guard<std::mutex> locker(m_mutex);
  251. return m_queue.size();
  252. }
  253. int Count()
  254. {
  255. return m_queue.size();
  256. }
  257. private:
  258. bool NotFull() const
  259. {
  260. bool full = m_queue.size() >= m_maxSize;
  261. if (full)
  262. std::cout << "full, waiting, thread id: " << std::this_thread::get_id() << std::endl;
  263. return !full;
  264. }
  265. bool NotEmpty() const
  266. {
  267. bool empty = m_queue.empty();
  268. if (empty)
  269. std::cout << "empty,waiting, thread id: " << std::this_thread::get_id() << std::endl;
  270. return !empty;
  271. }
  272. template<typename F>
  273. void Add(F&&x)
  274. {
  275. std::unique_lock< std::mutex> locker(m_mutex);
  276. m_notFull.wait(locker, [this] {return m_needStop || NotFull(); });
  277. if (m_needStop) return;
  278. m_queue.push_back(std::forward<F>(x));
  279. m_notEmpty.notify_one();
  280. }
  281. private:
  282. std::list<T> m_queue; //缓冲区
  283. std::mutex m_mutex; //互斥量和条件变量结合起来使用
  284. std::condition_variable m_notEmpty;//不为空的条件变量
  285. std::condition_variable m_notFull; //没有满的条件变量
  286. int m_maxSize; //同步队列最大的size
  287. bool m_needStop; //停止的标志
  288. };
  289. const int MaxTaskCount = 100;
  290. class ThreadPool {
  291. public:
  292. using Task = std::function<void()>;
  293. ThreadPool(int numThreads = std::thread::hardware_concurrency()) : m_queue(MaxTaskCount)
  294. {
  295. Start(numThreads);
  296. }
  297. ~ThreadPool(void)
  298. {
  299. //如果没有停止时则主动停止线程池
  300. Stop();
  301. }
  302. void Stop()
  303. {
  304. std::call_once(m_flag, [this] {StopThreadGroup(); }); //保证多线程情况下只调用一次StopThreadGroup
  305. }
  306. void AddTask(Task&&task)
  307. {
  308. m_queue.Put(std::forward<Task>(task));
  309. }
  310. void AddTask(const Task& task)
  311. {
  312. m_queue.Put(task);
  313. }
  314. private:
  315. void Start(int numThreads)
  316. {
  317. m_running = true;
  318. //创建线程组
  319. for (int i = 0; i <numThreads; ++i) {
  320. m_threadgroup.push_back(std::make_shared<std::thread>(&ThreadPool::RunInThread, this));
  321. }
  322. }
  323. void RunInThread()
  324. {
  325. while (m_running) {
  326. //取任务分别执行
  327. std::list<Task> list;
  328. m_queue.Take(list);
  329. for (auto& task : list) {
  330. if (!m_running)
  331. return;
  332. task();
  333. }
  334. }
  335. }
  336. void StopThreadGroup()
  337. {
  338. m_queue.Stop(); //让同步队列中的线程停止
  339. m_running = false; //置为false,让内部线程跳出循环并退出
  340. for (auto thread : m_threadgroup) { //等待线程结束
  341. if (thread)
  342. thread->join();
  343. }
  344. m_threadgroup.clear();
  345. }
  346. std::list<std::shared_ptr<std::thread>> m_threadgroup; //处理任务的线程组
  347. SyncQueue<Task> m_queue; //同步队列
  348. std::atomic_bool m_running; //是否停止的标志
  349. std::once_flag m_flag;
  350. };
  351. void TestThdPool()
  352. {
  353. ThreadPool pool; bool runing = true;
  354. std::thread thd1([&pool, &runing] {
  355. while (runing) {
  356. std::cout << "produce " << std::this_thread::get_id() << std::endl;
  357. pool.AddTask([] {
  358. std::cout << "consume " << std::this_thread::get_id() << std::endl;
  359. });
  360. }
  361. });
  362. std::this_thread::sleep_for(std::chrono::seconds(10));
  363. runing = false;
  364. pool.Stop();
  365. thd1.join();
  366. getchar();
  367. }
  368. int test_thread_pool()
  369. {
  370. TestThdPool();
  371. return 0;
  372. }
  373. //
  374. int test_thread_hardware_concurrency()
  375. {
  376. std::cout << " the number of hardware thread contexts: " << std::thread::hardware_concurrency() << std::endl;
  377. return 0;
  378. }
  379. #endif
  380. //
  381. // reference: https://thispointer.com/c-11-multithreading-part-1-three-different-ways-to-create-threads/
  382. // creating a thread using function objects
  383. class DisplayThread {
  384. public:
  385. void operator()()
  386. {
  387. for (int i = 0; i < 10; ++i)
  388. std::cout<<"Display Thread Executing"<<std::endl;
  389. }
  390. };
  391. int test_thread_1()
  392. {
  393. std::thread threadObj((DisplayThread()));
  394. for (int i = 0; i < 10; ++i)
  395. std::cout<<"Display From Main Thread "<<std::endl;
  396. std::cout<<"Waiting For Thread to complete"<<std::endl;
  397. threadObj.join();
  398. std::cout<<"Exiting from Main Thread"<<std::endl;
  399. return 0;
  400. }
  401. // reference: https://thispointer.com/c-11-multithreading-part-1-three-different-ways-to-create-threads/
  402. // creating a thread using lambda functions
  403. int test_thread_2()
  404. {
  405. int x = 9;
  406. std::thread threadObj([]{
  407. for(int i = 0; i < 10; i++)
  408. std::cout<<"Display Thread Executing"<<std::endl;
  409. });
  410. for(int i = 0; i < 10; i++)
  411. std::cout<<"Display From Main Thread"<<std::endl;
  412. threadObj.join();
  413. std::cout<<"Exiting from Main Thread"<<std::endl;
  414. return 0;
  415. }
  416. // reference: https://stackoverflow.com/questions/10673585/start-thread-with-member-function
  417. // start thread with member function
  418. class bar {
  419. public:
  420. void foo() {
  421. std::cout << "hello from member function" << std::endl;
  422. }
  423. };
  424. int test_thread_3()
  425. {
  426. std::thread t(&bar::foo, bar());
  427. t.join();
  428. return 0;
  429. }
  430. bool flag1 = true, flag2 = true;
  431. void print_xxx()
  432. {
  433. while (1) {
  434. if (!flag1) break;
  435. std::this_thread::sleep_for(std::chrono::seconds(1));
  436. fprintf(stdout, "print xxx\n");
  437. }
  438. }
  439. void print_yyy()
  440. {
  441. while (1) {
  442. if (!flag2) break;
  443. std::this_thread::sleep_for(std::chrono::seconds(2));
  444. fprintf(stdout, "print yyy\n");
  445. }
  446. }
  447. int test_thread_4()
  448. {
  449. std::thread th1(print_xxx);
  450. std::thread th2(print_yyy);
  451. std::this_thread::sleep_for(std::chrono::minutes(2));
  452. flag1 = false;
  453. flag2 = false;
  454. th1.join();
  455. th2.join();
  456. return 0;
  457. }
  458. } // namespace::thread_

GitHub:https://github.com/fengbingchun/Messy_Test

转载自:https://blog.csdn.net/fengbingchun/article/details/73393229

Multi-thread--C++11中thread的使用相关推荐

  1. c++11中thread join和detach的区别

    线程状态: 在一个线程的生存期内,可以在多种状态之间转换,不同的操作系统可以实现不同的线程模型,定义许多不同的线程状态,每个状态还可以包含多个子状态,但大体来说,如下几种状态是通用的: 1)就绪:参与 ...

  2. java thread join()_Java中Thread.join()的使用方法

    概要 本文分三个部分对thread.join()进行分析: 1. join() 的示例和作用 2. join() 源码分析 3. 对网上其他分析 join() 的文章提出疑问 1. join() 的示 ...

  3. java thread和runnable_java中thread和runnable的区别

    展开全部 线程的起动62616964757a686964616fe4b893e5b19e31333361326332并不是简单的调用了你的RUN方法,而是由一个线程调度器来分别调用你的所有线程的RUN ...

  4. C++11中Thread类简单使用的例子

    代码如下: #include <iostream> #include <thread> #include <chrono> #include <future& ...

  5. C++11中头文件thread的使用

    C++11中加入了<thread>头文件,此头文件主要声明了std::thread线程类.C++11的标准类std::thread对线程进行了封装.std::thread代表了一个线程对象 ...

  6. Java中Thread中的实例方法_Java多线程2:Thread中的实例方法

    Thread类中的方法调用方式: 学习Thread类中的方法是学习多线程的第一步.在学习多线程之前特别提出一点,调用Thread中的方法的时候,在线程类中,有两种方式,一定要理解这两种方式的区别: 1 ...

  7. java中thread实例_Java多线程2:Thread中的实例方法

    Thread类中的方法调用方式: 学习Thread类中的方法是学习多线程的第一步.在学习多线程之前特别提出一点,调用Thread中的方法的时候,在线程类中,有两种方式,一定要理解这两种方式的区别: 1 ...

  8. [C++11 std::thread] 使用C++11 编写 Linux 多线程程序

    From: http://www.ibm.com/developerworks/cn/linux/1412_zhupx_thread/index.html 本文讲述了如何使用 C++11 编写 Lin ...

  9. [C++] - C++11 多线程 - Thread

    转载整理自:https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/tree/master/zh/chapter3-Thread 1 ...

最新文章

  1. java制作文本框中的表格输入List数据
  2. how is central js retrieved from ABAP repository
  3. linux安装mysql5.7.18_Linux下安装mysql5.7.18版本步骤
  4. 【HDU - 5649】DZY Loves Sorting(线段树,区间更新区间查询,思维,01缩数变换,线段树分割)
  5. Flink本地安装教程
  6. Introduction to Computer Networking学习笔记(八):end-to-end principle端对端原则
  7. 中科院-杨力祥视频教程 04课程
  8. 仿视频字幕弹幕网站 – Miko二次元动漫视频网站源码 视频播放带源码
  9. 计算机网络专业综合实践报告,计算机网络专业实习报告.doc
  10. android 弹幕stg模板,STG游戏在弹幕设计上有何讲究?
  11. 转!!以太网方案设计
  12. 上市公司创新研发支出数据(2006-2018年)
  13. 由浅入深MFC学习摘记--第三部分
  14. html图片大小单位,mm单位是什么?
  15. 【论文导读】- Subgraph Federated Learning with Missing Neighbor Generation(FedSage、FedSage+)
  16. 我的程序人生——初识代码,从学好C语言开始
  17. Xilinx ZYNQ Ultrascale+ 性能测试之 PL/PS PCIe Root Port NVMe
  18. 计算机屏幕大小怎么计算,电视机尺寸怎么算 电视机的尺寸换算公式
  19. 苹果手机打字换行怎么换_苹果id怎么换
  20. 干货|app自动化测试之Appium 原理 与 JsonWP 协议分析

热门文章

  1. 2019国内某知名科技公司技术资料
  2. java 课后习题 冒泡排序的运用
  3. 无法启动ServletWebServerApplicatio错误记录
  4. C#LeetCode刷题之#59-螺旋矩阵 II(Spiral Matrix II)
  5. C#LeetCode刷题-排序
  6. 服务器控件的优点和缺点_什么是无服务器架构? 它的优点和缺点是什么?
  7. gatsby_将Gatsby默认启动程序转换为使用样式化组件
  8. lynda ux_举办UX午餐并学习并成为UX英雄
  9. linux升级openssh8.2,openssh7更换升级位8.2版本过程
  10. python调试神器_python调试神器PySnooper的使用