C++在多線程中使用condition_variable實(shí)現(xiàn)wait
前言
有這樣的需求
一個(gè)線程需要等待另一個(gè)線程執(zhí)行完畢之后它才會(huì)繼續(xù)向下執(zhí)行,這該如何實(shí)現(xiàn)? condition_variable類中實(shí)現(xiàn)了wait方法
wait()
- 可以接受兩個(gè)參數(shù),其中第一個(gè)參數(shù)是鎖對(duì)象,第二個(gè)參數(shù)是lambda表達(dá)式,其中的lambda表達(dá)式的返回值要是bool類型
- 也可接受一個(gè)參數(shù),僅僅接受鎖對(duì)象
#include<iostream> #include<mutex> #include<list> #include<thread> using namespace std; class Obj { private: list<int> myList; condition_variable var; mutex tex; public: bool popFromList(int & comb) { if (!myList.empty()) { unique_lock<mutex> cur(tex); if (!myList.empty()) { comb = myList.front(); myList.pop_front(); return true; } } return false; } void inToList() { for (int i = 0; i < 100; i++) { unique_lock<mutex> cur(tex); myList.push_back(i); cout << this_thread::get_id() << "正在向list中添加數(shù)據(jù)>; " << i << endl; var.notify_one(); } } void outFromList() { while(true) { unique_lock<mutex> cur(tex); var.wait(cur, [this] { if (!myList.empty()) return true; return false; }); int comb = myList.front(); myList.pop_front(); cout << this_thread::get_id() << "正在取出數(shù)據(jù)>: " << comb << endl; } } }; int main() { Obj obj; thread thread1(&Obj::outFromList, &obj); thread thread2(&Obj::inToList, &obj); thread1.join(); thread2.join(); cout << "這里是主線程" << endl; }
線程1執(zhí)行到wait函數(shù)時(shí),會(huì)執(zhí)行l(wèi)ambda表達(dá)式
返回值為false時(shí),這個(gè)線程就會(huì)將鎖打開,將該線程壓入到棧中,執(zhí)行下一個(gè)線程 下一個(gè)線程執(zhí)行完畢之后執(zhí)行notify_one后就會(huì)返回到該線程,繼續(xù)執(zhí)行l(wèi)ambda表達(dá)式
返回值為true時(shí)就嘗試獲得鎖
獲得鎖后繼續(xù)執(zhí)行下面的語(yǔ)句
沒有獲得鎖就繼續(xù)卡在wait等待獲取到鎖
返回值為false時(shí),繼續(xù)將鎖打開,線程壓入棧,執(zhí)行下一個(gè)線程
返回值為true時(shí),線程繼續(xù)執(zhí)行
運(yùn)行結(jié)果可能是一個(gè)線程執(zhí)行完畢另一個(gè)線程再執(zhí)行
這不就是順序執(zhí)行嗎?
其實(shí)并不是這樣的
notify_one()
喚醒wait()函數(shù),當(dāng)前所再的線程嘗試獲得鎖
某個(gè)線程執(zhí)行完notify_one函數(shù)后,會(huì)返回到另一個(gè)線程中的wait函數(shù)處,并將其"喚醒",讓其繼續(xù)執(zhí)行,自己的線程和wait線程都嘗試獲得鎖來進(jìn)行下一步執(zhí)行
不是順序執(zhí)行的解釋
- 當(dāng)wait函數(shù)后面有很多語(yǔ)句要執(zhí)行,但是再此之前wait所在的線程函數(shù)中就已經(jīng)將鎖進(jìn)行釋放了,那么notify_one的“喚醒”就不在生效,兩個(gè)線程都嘗試獲得鎖,但是wait所在的線程有很多語(yǔ)句要執(zhí)行,耗時(shí)高,那么很有可能notify_one所在的線程就再次獲得了鎖,進(jìn)行下一步操作。
- 這樣就不一定是順序執(zhí)行的宏觀表現(xiàn)了
#include<iostream> #include<mutex> #include<list> #include<thread> using namespace std; class Obj { private: list<int> myList; condition_variable var; mutex tex; public: bool popFromList(int & comb) { if (!myList.empty()) { unique_lock<mutex> cur(tex); if (!myList.empty()) { comb = myList.front(); myList.pop_front(); return true; } } return false; } void inToList() { for (int i = 0; i < 1000; i++) { cout << this_thread::get_id() << "正在向list中添加數(shù)據(jù)>; " << i << endl; unique_lock<mutex> cur(tex); myList.push_back(i); var.notify_one(); } } void outFromList() { while(true) { unique_lock<mutex> cur(tex); var.wait(cur, [this] { if (!myList.empty()) return true; return false; }); int comb = myList.front(); myList.pop_front(); cur.unlock(); cout << this_thread::get_id() << "正在取出數(shù)據(jù)>: " << comb << endl; chrono::seconds tim(2); this_thread::sleep_for(tim); cout <<this_thread::get_id() << "睡眠兩秒后執(zhí)行" << endl; } } }; int main() { Obj obj; thread thread1(&Obj::outFromList, &obj); thread thread2(&Obj::inToList, &obj); thread1.join(); thread2.join(); cout << "這里是主線程" << endl; }
如圖所示,在notify_one線程執(zhí)行835次循環(huán)后,wait所在的線程才獲得了鎖,繼續(xù)執(zhí)行
在此之前的所有notify_one的喚醒操作都是無效的。
然后在notify_one線程執(zhí)行完畢之后,wait再次獲得了鎖,繼續(xù)向下執(zhí)行
notify_all()
顧名思義,是“喚醒”所有的wait函數(shù)線程,但是并不是都一起運(yùn)行,因?yàn)樗麄円M(jìn)行鎖的請(qǐng)求,僅僅只能由一把鎖存在,只有獲得鎖并且lambda表達(dá)式返回結(jié)果為true的線程才能繼續(xù)執(zhí)行
到此這篇關(guān)于C++在多線程中使用condition_variable實(shí)現(xiàn)wait的文章就介紹到這了,更多相關(guān)C++ condition_variable內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中測(cè)試程序運(yùn)行時(shí)間的幾種方法總結(jié)
本文介紹了C++中測(cè)量程序運(yùn)行時(shí)間的幾種方法,包括使用GetTickCount()、clock()、Boost庫(kù)的timer類以及高精度時(shí)控函數(shù)QueryPerformanceFrequency和QueryPerformanceCounter,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-09-09簡(jiǎn)單分析針對(duì)ARM平臺(tái)的C語(yǔ)言程序的編譯問題
這篇文章主要介紹了針對(duì)ARM平臺(tái)的C語(yǔ)言程序的編譯問題,從優(yōu)化編譯選項(xiàng)的幾個(gè)方面進(jìn)行分析,需要的朋友可以參考下2015-12-12Windows安裝Qt6.4.2及簡(jiǎn)單驗(yàn)證
本文主要介紹了Windows安裝Qt6.4.2及簡(jiǎn)單驗(yàn)證,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02C++實(shí)現(xiàn)經(jīng)典24點(diǎn)紙牌益智游戲
這篇文章主要介紹了C++實(shí)現(xiàn)經(jīng)典24點(diǎn)紙牌益智游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)與算法時(shí)間空間復(fù)雜度基礎(chǔ)實(shí)踐
這篇文章主要為大家介紹了C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)與算法中時(shí)間空間復(fù)雜度的基礎(chǔ)實(shí)踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02Qt中QSettings配置文件的讀寫和應(yīng)用場(chǎng)景詳解
這篇文章主要給大家介紹了關(guān)于Qt中QSettings配置文件的讀寫和應(yīng)用場(chǎng)景的相關(guān)資料,QSettings能讀寫配置文件,當(dāng)配置文件不存在時(shí),可生成配置文件,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10