https://www.cnblogs.com/whlook/p/6573659.html
(https://www.cnblogs.com/lidabo/p/7852033.html)
C++:線程(std::thread)
1.創建一個線程
創建線程比較簡單,使用std的thread實例化一個線程對象就創建完成了,示例:
#include <iostream> #include <thread> using namespace std; void t1() //普通的函數,用來執行線程 { for (int i = 0; i < 20; ++i) { cout << "t1111\n"; } } void t2() { for (int i = 0; i < 20; ++i) { cout << "t22222\n"; } } int main() { thread th1(t1); //實例化一個線程對象th1,使用函數t1構造,然后該線程就開始執行了(t1()) thread th2(t2); cout << "here is main\n\n"; return 0; }
不過這個示例是有問題的,因為在創建了線程后線程開始執行,但是主線程main()並沒有停止腳步,仍然繼續執行然后退出,此時線程對象還是joinable的,線程仍然存在但指向它的線程對象已經銷毀,所以會拋出異常。
那么該如何保證子線程執行完了退出后再退出主線程呢?
2.thread::join()
使用join接口可以解決上述問題,join的作用是讓主線程等待直到該子線程執行結束,示例:
#include <iostream> #include <thread> using namespace std; void t1() { for (int i = 0; i < 20; ++i) { cout << "t1111\n"; } } void t2() { for (int i = 0; i < 20; ++i) { cout << "t22222\n"; } } int main() { thread th1(t1); thread th2(t2); th1.join(); //等待th1執行完 th2.join(); //等待th2執行完 cout << "here is main\n\n"; return 0; }
此時就可以正常地執行子線程了,同時注意最后一個輸出,說明了main是等待子線程結束才繼續執行的
需要注意的是線程對象執行了join后就不再joinable了,所以只能調用join一次。
3.thread::detach()
(1.)中提到的問題,還可以使用detach來解決,detach是用來和線程對象分離的,這樣線程可以獨立地執行,不過這樣由於沒有thread對象指向該線程而失去了對它的控制,當對象析構時線程會繼續在后台執行,但是當主程序退出時並不能保證線程能執行完。如果沒有良好的控制機制或者這種后台線程比較重要,最好不用detach而應該使用join。
int main() { thread th1(t1); thread th2(t2); th1.detach(); th2.detach(); cout << "here is main\n\n"; return 0; }
由結果可見線程並沒有執行完而退出:
4.mutex
頭文件是<mutex>,mutex是用來保證線程同步的,防止不同的線程同時操作同一個共享數據。
但是使用mutex是不安全的,當一個線程在解鎖之前異常退出了,那么其它被阻塞的線程就無法繼續下去。
5.std::lock_guard
使用lock_guard則相對安全,它是基於作用域的,能夠自解鎖,當該對象創建時,它會像m.lock()一樣獲得互斥鎖,當生命周期結束時,它會自動析構(unlock),不會因為某個線程異常退出而影響其他線程。示例:
int cnt = 20; mutex m; void t1() { while (cnt > 0) { lock_guard<mutex> lockGuard(m); if (cnt > 0) { --cnt; cout << cnt << endl; } } } void t2() { while (cnt > 0) { lock_guard<mutex> lockGuard(m); if (cnt > 0) { --cnt; cout << cnt << endl; } } }