C++11併發之std::thread

知識連接:
C++11 併發之std::atomic
 
本文概要:
一、成員類型和成員函數。
 
二、std::thread 構造函數。
三、異步。
四、多線程傳遞參數。
五、join、detach。
六、獲取CPU核心個數。
七、CPP原子變量與線程安全。
八、lambda與多線程。
九、時間等待相關問題。
十、線程功能拓展。
十一、多線程可變參數。
十二、線程交換。
1三、線程移動。
 
std::thread 在 #include<thread> 頭文件中聲明,所以使用 std::thread 時須要包含 #include<thread> 頭文件。
 
一、成員類型和成員函數。
成員類型:

成員函數:

Non-member overloads:

 
二、std::thread 構造函數。
以下表:
(1).默認構造函數,建立一個空的 thread 執行對象。
(2).初始化構造函數,建立一個 thread 對象,該 thread 對象可被 joinable,新產生的線程會調用 fn 函數,該函數的參數由 args 給出。
(3).拷貝構造函數(被禁用),意味着 thread 不可被拷貝構造。
(4).move 構造函數,move 構造函數,調用成功以後 x 不表明任何 thread 執行對象。
注意:可被 joinable 的 thread 對象必須在他們銷燬以前被主線程 join 或者將其設置爲 detached。
 
std::thread 各類構造函數例子以下:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. #include<chrono>  
  4. using namespace std;  
  5. void fun1(int n)  //初始化構造函數  
  6. {  
  7.     cout << "Thread " << n << " executing\n";  
  8.     n += 10;  
  9.     this_thread::sleep_for(chrono::milliseconds(10));  
  10. }  
  11. void fun2(int & n) //拷貝構造函數  
  12. {  
  13.     cout << "Thread " << n << " executing\n";  
  14.     n += 20;  
  15.     this_thread::sleep_for(chrono::milliseconds(10));  
  16. }  
  17. int main()  
  18. {  
  19.     int n = 0;  
  20.     thread t1;               //t1不是一個thread  
  21.     thread t2(fun1, n + 1);  //按照值傳遞  
  22.     t2.join();  
  23.     cout << "n=" << n << '\n';  
  24.     n = 10;  
  25.     thread t3(fun2, ref(n)); //引用  
  26.     thread t4(move(t3));     //t4執行t3,t3不是thread  
  27.     t4.join();  
  28.     cout << "n=" << n << '\n';  
  29.     return 0;  
  30. }  
  31. 運行結果:  
  32. Thread 1 executing  
  33. n=0  
  34. Thread 10 executing  
  35. n=30</span>  
 
三、異步。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. void show()  
  5. {  
  6.     cout << "hello cplusplus!" << endl;  
  7. }  
  8. int main()  
  9. {  
  10.     //棧上  
  11.     thread t1(show);   //根據函數初始化執行  
  12.     thread t2(show);  
  13.     thread t3(show);  
  14.     //線程數組  
  15.     thread th[3]{thread(show), thread(show), thread(show)};   
  16.     //堆上  
  17.     thread *pt1(new thread(show));  
  18.     thread *pt2(new thread(show));  
  19.     thread *pt3(new thread(show));  
  20.     //線程指針數組  
  21.     thread *pth(new thread[3]{thread(show), thread(show), thread(show)});  
  22.     return 0;  
  23. }</span>  
 
四、多線程傳遞參數。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. void show(const char *str, const int id)  
  5. {  
  6.     cout << "線程 " << id + 1 << " :" << str << endl;  
  7. }  
  8. int main()  
  9. {  
  10.     thread t1(show, "hello cplusplus!", 0);  
  11.     thread t2(show, "你好,C++!", 1);  
  12.     thread t3(show, "hello!", 2);  
  13.     return 0;  
  14. }  
  15. 運行結果:  
  16. 線程 1線程 2 :你好,C++!線程 3 :hello!  
  17. :hello cplusplus!</span>  
發現,線程 t一、t二、t3 都執行成功!
 
五、join、detach。
join例子以下:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. #include<array>  
  4. using namespace std;  
  5. void show()  
  6. {  
  7.     cout << "hello cplusplus!" << endl;  
  8. }  
  9. int main()  
  10. {  
  11.     array<thread, 3>  threads = { thread(show), thread(show), thread(show) };  
  12.     for (int i = 0; i < 3; i++)  
  13.     {  
  14.         cout << threads[i].joinable() << endl;//判斷線程是否能夠join  
  15.         threads[i].join();//主線程等待當前線程執行完成再退出  
  16.     }  
  17.     return 0;  
  18. }  
  19. 運行結果:  
  20. hello cplusplus!  
  21. hello cplusplus!  
  22. 1  
  23. hello cplusplus!  
  24. 1  
  25. 1</span>  
總結:
join 是讓當前主線程等待全部的子線程執行完,才能退出。
detach例子以下:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. void show()  
  5. {  
  6.     cout << "hello cplusplus!" << endl;  
  7. }  
  8. int main()  
  9. {  
  10.     thread th(show);  
  11.     //th.join();   
  12.     th.detach();//脫離主線程的綁定,主線程掛了,子線程不報錯,子線程執行完自動退出。  
  13.     //detach之後,子線程會成爲孤兒線程,線程之間將沒法通訊。  
  14.     cout << th.joinable() << endl;  
  15.     return 0;  
  16. }  
  17. 運行結果:  
  18. hello cplusplus!  
  19. 0</span>  
結論:
線程 detach 脫離主線程的綁定,主線程掛了,子線程不報錯,子線程執行完自動退出。
線程 detach之後,子線程會成爲孤兒線程,線程之間將沒法通訊。
 
六、獲取CPU核心個數。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.     auto n = thread::hardware_concurrency();//獲取cpu核心個數  
  7.     cout << n << endl;  
  8.     return 0;  
  9. }  
  10. 運行結果:  
  11. 8</span>  
結論:
經過  thread::hardware_concurrency() 獲取 CPU 核心的個數。
 
七、CPP原子變量與線程安全。
問題例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. const int N = 100000000;  
  5. int num = 0;  
  6. void run()  
  7. {  
  8.     for (int i = 0; i < N; i++)  
  9.     {  
  10.         num++;  
  11.     }  
  12. }  
  13. int main()  
  14. {  
  15.     clock_t start = clock();  
  16.     thread t1(run);  
  17.     thread t2(run);  
  18.     t1.join();  
  19.     t2.join();  
  20.     clock_t end = clock();  
  21.     cout << "num=" << num << ",用時 " << end - start << " ms" << endl;  
  22.     return 0;  
  23. }  
  24. 運行結果:  
  25. num=143653419,用時 730 ms</span>  
從上述代碼執行的結果,發現結果並非咱們預計的200000000,這是因爲線程之間發生衝突,從而致使結果不正確。
爲了解決此問題,有如下方法:
(1)互斥量。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. #include<mutex>  
  4. using namespace std;  
  5. const int N = 100000000;  
  6. int num(0);  
  7. mutex m;  
  8. void run()  
  9. {  
  10.     for (int i = 0; i < N; i++)  
  11.     {  
  12.         m.lock();  
  13.         num++;  
  14.         m.unlock();  
  15.     }  
  16. }  
  17. int main()  
  18. {  
  19.     clock_t start = clock();  
  20.     thread t1(run);  
  21.     thread t2(run);  
  22.     t1.join();  
  23.     t2.join();  
  24.     clock_t end = clock();  
  25.     cout << "num=" << num << ",用時 " << end - start << " ms" << endl;  
  26.     return 0;  
  27. }  
  28. 運行結果:  
  29. num=200000000,用時 128323 ms</span>  
不難發現,經過互斥量後運算結果正確,可是計算速度很慢,緣由主要是互斥量加解鎖須要時間。
互斥量詳細內容 請參考 C++11 併發之std::mutex
(2)原子變量。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. #include<atomic>  
  4. using namespace std;  
  5. const int N = 100000000;  
  6. atomic_int num{ 0 };//不會發生線程衝突,線程安全  
  7. void run()  
  8. {  
  9.     for (int i = 0; i < N; i++)  
  10.     {  
  11.         num++;  
  12.     }  
  13. }  
  14. int main()  
  15. {  
  16.     clock_t start = clock();  
  17.     thread t1(run);  
  18.     thread t2(run);  
  19.     t1.join();  
  20.     t2.join();  
  21.     clock_t end = clock();  
  22.     cout << "num=" << num << ",用時 " << end - start << " ms" << endl;  
  23.     return 0;  
  24. }  
  25. 運行結果:  
  26. num=200000000,用時 29732 ms</span>  
不難發現,經過原子變量後運算結果正確,計算速度通常。
原子變量詳細內容 請參考C++11 併發之std::atomic。
(3)加入 join 。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. const int N = 100000000;  
  5. int num = 0;  
  6. void run()  
  7. {  
  8.     for (int i = 0; i < N; i++)  
  9.     {  
  10.         num++;  
  11.     }  
  12. }  
  13. int main()  
  14. {  
  15.     clock_t start = clock();  
  16.     thread t1(run);  
  17.     t1.join();  
  18.     thread t2(run);  
  19.     t2.join();  
  20.     clock_t end = clock();  
  21.     cout << "num=" << num << ",用時 " << end - start << " ms" << endl;  
  22.     return 0;  
  23. }  
  24. 運行結果:  
  25. num=200000000,用時 626 ms</span>  
不難發現,經過原子變量後運算結果正確,計算速度也很理想。
 
八、lambda與多線程。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.     auto fun = [](const char *str) {cout << str << endl; };  
  7.     thread t1(fun, "hello world!");  
  8.     thread t2(fun, "hello beijing!");  
  9.     return 0;  
  10. }  
  11. 運行結果:  
  12. hello world!  
  13. hello beijing!</span>  
九、時間等待相關問題。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. #include<chrono>  
  4. using namespace std;  
  5. int main()  
  6. {  
  7.     thread th1([]()  
  8.     {  
  9.         //讓線程等待3秒  
  10.         this_thread::sleep_for(chrono::seconds(3));  
  11.         //讓cpu執行其餘空閒的線程  
  12.         this_thread::yield();  
  13.         //線程id  
  14.         cout << this_thread::get_id() << endl;  
  15.     });  
  16.     return 0;  
  17. }</span>  
十、線程功能拓展。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. class MyThread :public thread   //繼承thread  
  5. {  
  6. public:  
  7.     //子類MyThread()繼承thread()的構造函數  
  8.     MyThread() : thread()  
  9.     {  
  10.     }  
  11.     //MyThread()初始化構造函數  
  12.     template<typename T, typename...Args>  
  13.     MyThread(T&&func, Args&&...args) : thread(forward<T>(func), forward<Args>(args)...)  
  14.     {  
  15.     }  
  16.     void showcmd(const char *str)  //運行system  
  17.     {  
  18.         system(str);  
  19.     }  
  20. };  
  21. int main()  
  22. {  
  23.     MyThread th1([]()  
  24.     {  
  25.         cout << "hello" << endl;  
  26.     });  
  27.     th1.showcmd("calc"); //運行calc  
  28.     //lambda  
  29.     MyThread th2([](const char * str)  
  30.     {  
  31.         cout << "hello" << str << endl;  
  32.     }, " this is MyThread");  
  33.     th2.showcmd("notepad");//運行notepad  
  34.     return 0;  
  35. }  
  36. 運行結果:  
  37. hello  
  38. //運行calc  
  39. hello this is MyThread  
  40. //運行notepad</span>  
 
十一、多線程可變參數。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. #include<cstdarg>  
  4. using namespace std;  
  5. int show(const char *fun, ...)  
  6. {  
  7.     va_list ap;//指針  
  8.     va_start(ap, fun);//開始  
  9.     vprintf(fun, ap);//調用  
  10.     va_end(ap);  
  11.     return 0;  
  12. }  
  13. int main()  
  14. {  
  15.     thread t1(show, "%s    %d    %c    %f", "hello world!", 100, 'A', 3.14159);  
  16.     return 0;  
  17. }  
  18. 運行結果:  
  19. hello world!    100    A    3.14159</span>  
 
十二、線程交換。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.     thread t1([]()  
  7.     {  
  8.         cout << "thread1" << endl;  
  9.     });  
  10.     thread t2([]()  
  11.     {  
  12.         cout << "thread2" << endl;  
  13.     });  
  14.     cout << "thread1' id is " << t1.get_id() << endl;  
  15.     cout << "thread2' id is " << t2.get_id() << endl;  
  16.       
  17.     cout << "swap after:" << endl;  
  18.     swap(t1, t2);//線程交換  
  19.     cout << "thread1' id is " << t1.get_id() << endl;  
  20.     cout << "thread2' id is " << t2.get_id() << endl;  
  21.     return 0;  
  22. }  
  23. 運行結果:  
  24. thread1  
  25. thread2  
  26. thread1' id is 4836  
  27. thread2' id is 4724  
  28. swap after:  
  29. thread1' id is 4724  
  30. thread2' id is 4836</span>  
兩個線程經過 swap 進行交換。
 
1三、線程移動。
例如:
[cpp]  view plain  copy
 
  1. <span style="font-size:12px;">#include<iostream>  
  2. #include<thread>  
  3. using namespace std;  
  4. int main()  
  5. {  
  6.     thread t1([]()  
  7.     {  
  8.         cout << "thread1" << endl;  
  9.     });  
  10.     cout << "thread1' id is " << t1.get_id() << endl;  
  11.     thread t2 = move(t1);;  
  12.     cout << "thread2' id is " << t2.get_id() << endl;  
  13.     return 0;  
  14. }  
  15. 運行結果:  
  16. thread1  
  17. thread1' id is 5620  
  18. thread2' id is 5620</span>  
從上述代碼中,線程t2能夠經過 move 移動 t1 來獲取 t1 的所有屬性,而 t1 卻銷燬了。
相關文章
相關標籤/搜索