C++11并發與多線程筆記(8) condition_variable、wait、notify_one、notify_all
- 1、條件變量condition_variable、wait、notify_one、notify_all
- 1.1 std::condition_variable
- 1.2 wait()
- 1.3 notify_one()
- 1.4 notify_all()
- 2、深入思考
1、條件變量condition_variable、wait、notify_one、notify_all
1.1 std::condition_variable
實際上是一個類,是一個和條件相關的類,說白了就是等待一個條件達成。
1.2 wait()
-
wait()只有一個參數(相當于第二個參數為true)
void wait( std::unique_lock<std::mutex>& lock )
先解鎖之前獲得的互斥量mutex,然后阻塞當前的執行線程。把當前線程添加到等待線程列表中,該線程會持續阻塞直到被 ** notify_one()** 喚醒。被喚醒后,該thread會嘗試重新獲取互斥量mutex,獲取到mutex后執行后面的動作。
-
wait()有兩個參數
void wait( std::unique_lock<std::mutex>& lock, Predicate pred )
設置了第二個參數 Predicate, 只有當pred為false時,wait才會阻塞當前線程。這情況下,線程被喚醒后,先獲取mutex,再次判斷pred的值。如果pred為false,則會釋放mutex并重新阻塞在wait。
wait() 效果就是把鎖釋放并阻塞,以便于其他人去獲取,不然條件不滿足時,一遍遍去詢問,太浪費時間。
1.3 notify_one()
隨機喚醒一個等待的線程
notify_one() 喚醒一個線程不一定能成功,如果要喚醒的線程不在wait()處等待,將沒有喚醒效果。
1.4 notify_all()
喚醒所有等待的線程
#include <iostream>
#include <thread>
#include <mutex>
#include <list>
using namespace std;list<int> test_list;
mutex myMutex1;//創建一個互斥量condition_variable my_cond;//生成一個條件變量對象
void in_list() {for (int num = 0; num < 100000; num++) {{cout << "插入數據:" << num << endl;unique_lock<mutex> myUniLock(myMutex1);//操作事務test_list.push_back(num);my_cond.notify_one();//我們嘗試把wait()的線程喚醒,執行完這行,那么out_list()里邊的wait就會被喚醒}}
}void out_list() {int command = 0;while (true) {unique_lock<mutex> myUniLock(myMutex1);//如果第二個參數lambda表達式返回值是false,那么wait()將解鎖互斥量,并堵塞到本行,my_cond.wait(myUniLock, [=] {//一個lambda就是一個可調用的對象(函數)if (!test_list.empty())return true;return false;});//流程只要走到這里來,這個互斥鎖一定是鎖著的command= test_list.front();//返回第一個元素,但不檢查元素是否存在test_list.pop_front();//移除第一個元素,但不返回myUniLock.unlock();//可以提前解鎖,以免鎖住太長時間//執行其它非共享數據業務cout << "out_list()執行,取出一個元素" << command << endl;}
}
int main()
{thread in_thread(in_list);thread out_thread(out_list);out_thread.join();in_thread.join();cout << "I love China!" << endl;return 0;
}
2、深入思考
上面的代碼可能導致出現一種情況:
因為out_list()與in_list()并不是一對一執行的,所以當程序循環執行很多次以后,可能在test_list中已經有了很多消息,但是,out_list還是被喚醒一次只處理一條數據。這時可以考慮把out_list多執行幾次,或者對in_list進行限流。