C++11条件变量
- 條件變量
- 成員函數(shù)
- 代碼測(cè)試
條件變量
條件變量(condition variable)是利用線程間共享的全局變量進(jìn)行同步的一種機(jī)制。
包括兩個(gè)動(dòng)作:
一個(gè)線程等待某個(gè)條件為真,而將自己掛起;另一個(gè)線程使得條件成立,并通知等待的線程繼續(xù)運(yùn)行。
為了防止競(jìng)爭(zhēng),條件變量的使用總是和一個(gè)互斥鎖結(jié)合在一起。
C++11 中引入了條件變量,其相關(guān)內(nèi)容均在<condition_variable>中。
這里介紹 std::condition_variable 類。
條件變量std::condition_variable 用于多線程之間的通信,它可以阻塞一個(gè)或同時(shí)阻塞多個(gè)線程。
std::condition_variable 需要與 std::unique_lock 配合使用。
std::condition_variable 效果上相當(dāng)于包裝了 pthread 庫中的pthread_cond_*()系列的函數(shù)。
當(dāng)std::condition_variable 對(duì)象的某個(gè) wait 函數(shù)被調(diào)用的時(shí)候, 它使用std::unique_lock(通過std::mutex)來鎖住當(dāng)前線程。
當(dāng)前線程會(huì)一直被阻塞,直到另外一個(gè)線程在相同的std::condition_variable 對(duì)象上調(diào)用了notification 函數(shù)來喚醒當(dāng)前線程。
成員函數(shù)
(1)、構(gòu)造函數(shù):僅支持默認(rèn)構(gòu)造函數(shù),拷貝、賦值和移動(dòng)(move)均是被禁用的。
(2)、wait:當(dāng)前線程調(diào)用wait()后將被阻塞,直到另外某個(gè)線程調(diào)用notify_*喚醒當(dāng)前線程;
當(dāng)線程被阻塞時(shí),該函數(shù)會(huì)自動(dòng)調(diào)用std::mutex 的unlock()釋放鎖,使得其它被阻塞在鎖競(jìng)爭(zhēng)上的線程得以繼續(xù)執(zhí)行。一旦當(dāng)前線程獲得通知(notify,通常是另外某個(gè)線程調(diào)用notify_*喚醒了當(dāng)前線程),wait()函數(shù)也是自動(dòng)調(diào)用 std::mutex 的lock()。wait 分為無條件被阻塞和帶條件的被阻塞兩種。
無條件被阻塞:調(diào)用該函數(shù)前,當(dāng)前線程應(yīng)該已經(jīng)對(duì)unique_lock<mutex> lck 完成了加鎖。所有使用同一個(gè)條件變量的線程必須在wait 函數(shù)中使用同一個(gè)unique_lock<mutex>。該wait 函數(shù)內(nèi)部會(huì)自動(dòng)調(diào)用lck.unlock()對(duì)互斥鎖解鎖,使得其他被阻塞在互斥鎖上的線程恢復(fù)執(zhí)行。使用本函數(shù)被阻塞的當(dāng)前線程在獲得通知(notified,通過別的線程調(diào)用notify_*系列的函數(shù))而被喚醒后,wait()函數(shù)恢復(fù)執(zhí)行并自動(dòng)調(diào)用lck.lock()對(duì)互斥鎖加鎖。
帶條件的被阻塞:wait 函數(shù)設(shè)置了謂詞(Predicate),只有當(dāng)pred 條件為false 時(shí)調(diào)用該wait 函數(shù)才會(huì)阻塞當(dāng)前線程,并且在收到其它線程的通知后只有當(dāng)pred 為true 時(shí)才會(huì)被解除阻塞。因此,等效于while (!pred()) wait(lck).
(3)、wait_for:與wait()類似,只是wait_for 可以指定一個(gè)時(shí)間段,在當(dāng)前線程收到通知或者指定的時(shí)間超時(shí)之前,該線程都會(huì)處于阻塞狀態(tài)。而一旦超時(shí)或者收到了其它線程的通知,wait_for 返回,剩下的步驟和wait 類似。
(4)、wait_until:與wait_for 類似,只是wait_until 可以指定一個(gè)時(shí)間點(diǎn),在當(dāng)前線程收到通知或者指定的時(shí)間點(diǎn)超時(shí)之前,該線程都會(huì)處于阻塞狀態(tài)。而一旦超時(shí)或者收到了其它線程的通知,wait_until 返回,剩下的處理步驟和wait 類似。
(5)、notify_all: 喚醒所有的wait 線程,如果當(dāng)前沒有等待線程,則該函數(shù)什么也不做。
(6)、notify_one:喚醒某個(gè)wait 線程,如果當(dāng)前沒有等待線程,則該函數(shù)什么也不做;
如果同時(shí)存在多個(gè)等待線程,則喚醒某個(gè)線程是不確定的(unspecified)。
代碼測(cè)試
條件:
10 個(gè)線程,爭(zhēng)相去打印傳入的id,第一個(gè)線程獲得鎖,但是條件不滿足,采用條件變量出讓鎖,等待條件,其它線程亦是如此。
延時(shí)后的主要線程,獲得鎖,將條件置為真,并能過條件變量喚醒所有等待在條件變量上的10 個(gè)線程。
此時(shí)10 個(gè)線程再次爭(zhēng)相去獲取鎖,然后判斷條件為真,然后打印,釋放鎖給其它線程。
代碼演示:
#include <iostream> #include <thread> #include <chrono> #include <mutex> #include <condition_variable>using namespace std;condition_variable cv;mutex mtx;bool ready = false;void printId(int id) {unique_lock<mutex> ql(mtx);while (!ready){cv.wait(ql);}cout << "thread id:" << this_thread::get_id()<< " id = " << id << endl; }void go() {unique_lock<mutex> ql(mtx); //主線程中也要上鎖的。ready = true; //條件為真 才喚醒其他10個(gè)線程打印IDcv.notify_all(); }int main() {thread th[10];for (int i = 0; i < 10; i++){th[i] = thread(printId, i);}this_thread::sleep_for(chrono::seconds(5));go();for (auto& t : th){ t.join();}return 0; }運(yùn)行結(jié)果:
總結(jié)
- 上一篇: C++11线程
- 下一篇: C++11特性:override