1. 程式人生 > >詳解C++多線程(三)

詳解C++多線程(三)

頭文件 std 為什麽 col 函數 代碼 ret 過程 back

條件變量

這一章主要講講條件變量condition_variable。條件變量是一個非常神奇的存在,是線程間交互的一種方式。

C++11提供了condition_variable類。使用時需要include頭文件<condition_variable>。

如果把變量區看成是一座房子,那麽前面兩章頻繁用到的mutex可以看成是房門的鎖,正常來說是房門常年打開的,鎖並用不上。但是有了多線程以後,為了防止多個線程一窩蜂胡亂篡改裏面的數據,所以就有了鎖的概念。

現在假設每個線程都有一個管理鎖的人,叫lock_guard,或者unique_lock,但是一次只能有一個人能夠去操作鎖(鎖上或者是解鎖)。一般來說他們是輪流去操作鎖。而condition_variable則可以看做是門童,如果沒有滿足條件,門童就會通知線程的管鎖人必須要休眠而不可以操作鎖,可是一旦條件滿足,他就會喚醒某些線程的管鎖人可以去操作鎖了。

#include<thread>
#include<iostream>
#include<mutex>
#include<string>
#include<condition_variable>


using namespace std;

bool ready = false;
bool processed = false;
mutex mu;
condition_variable cv;
string data;

void worker_thread()
{
    unique_lock
<mutex> locker(mu); //ready = false,此處相當於全局變量區的門童通知t線程休眠 cv.wait(locker, [](){return ready;}); //ready = true,休眠結束。此時locker上鎖,開始修改變量 cout<<"start processing data"<<endl; data += " after processing"; processed = true; cout<<"worker thread has finished processing data
"<<endl; //門童通知其他線程全局變量的最新情況 //註意這一步是非常重要的,可以立即喚醒其他線程,否則其他線程會一直等待,這個過程可能會十分耗費時間 //這也是為什麽要用條件變量的原因 cv.notify_one(); //locker解鎖 } int main() { thread t(worker_thread); //啟動t線程 data = "example data"; unique_lock<mutex> locker(mu); //locker開始上鎖,main線程修改全局變量 ready = true; cout<<"main signals data ready for processing"<<endl; //門童通知其他線程全局變量的最新情況 cv.notify_one(); //locker解鎖 //processed = false, 門童通知main線程休眠 cv.wait(locker, [](){return processed;}); //processed = true, locker上鎖 cout<<"back in main, data = "<<data<<endl; //locker解鎖 t.join(); }

上面的代碼中需要註意一下幾點:

1. 代碼中的 [](){return ready;}是匿名函數,也可以用循環的寫法。

//cv.wait(locker, [](){return ready;});
    
 //或者寫成
while(ready==false)
    cv.wait(locker);

註意是while(ready == false),不是if(ready == false),因為wait的喚醒可能由於系統的原因被喚醒,這個的時機是不確定的。這個過程也被稱作偽喚醒(spurious wakeup)。

如果在錯誤的時候被喚醒,就開始執行了後面的操作就會造成錯誤。

2. 註意cv.wait() 和cv.notify_all()或者cv.notify_one()需要搭配使用才能真正發揮條件變量的作用。

3. cv.notify_one()指的是通知其中某一個線程,cv.notify_all()指的是通知全部線程。

參考:

  https://www.jianshu.com/p/c1dfa1d40f53

詳解C++多線程(三)