詳解C++多線程(三)
條件變量
這一章主要講講條件變量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++多線程(三)