C++ 多线程:条件变量 std::condition_variable
文章目錄
- 描述
- 使用
描述
-
頭文件
<condition_variable> -
定義
class condition_variable; -
簡介
之前我們也已經介紹過了C++多線程中互斥變量存在,已經足夠支持多線程中對共享普通系統數據的合理訪問。但是因為多線程中同一時刻必然會有一個線程持有鎖,一個線程等待鎖。而在代碼中使用while方式的循環等待必然會導致系統效率降低,cpu被無用消耗。
查看如下代碼#include <iostream> #include <thread> #include <string> #include <mutex> #include <deque> //包含deque的頭文件 using namespace std;std::mutex mu; std::deque<int> q; //全局共享數據,雙端隊列void function_1(){int count = 100;while (count > 0) {std::unique_lock<std::mutex> locker(mu);q.push_back(count);locker.unlock();std::this_thread::sleep_for(std::chrono::seconds(1));count--;} }void function_2() {int data = 0;while (data != 1) {std::unique_lock<std::mutex> locker(mu);//獲取到鎖之后檢測全局共享隊列是否為空,如果不為空則執行出隊操作if (!q.empty()) { data=q.back();q.pop_back();locker.unlock();std::cout << "t2 got a value from t1 " << data << std::endl; } else {//如果為空,則嘗試解鎖locker.unlock();//防止消耗cpu//std::this_thread::sleep_for(std::chrono::seconds(1)); }} } int main() {std::thread t1(function_1);std::thread t2(function_2);t1.join();t2.join();return 0; }由以上代碼可以很明顯得看出來t1線程提供數據
push_back(生產者),t2線程消費數據pop_back(消費者),這兩個線程最合理得運行方式應該為生產者生產好數據,然后喚醒消費者去消費,消費完成之后繼續休眠。但是在t2線程如果檢測到全局隊列為空,則會嘗試解鎖,但是在t1線程向全局隊列push數據的時候t2線程仍然在嘗試解鎖,這樣的操作會導致系統cpu資源被無端消耗,即消費者一直在等待著生產者生產。
這個時候我們的條件變量即可出山,它能夠比sleep_for函數使用起來更靈活
使用
在解決以上問題之前,先介紹幾個條件變量包含的成員函數
std::condition_variable::notify_one和std::condition_variable::notify_one通知一個等待線程,同時改函數作用在當前條件變量上std::condition_variable::wait()導致當前線程阻塞直至條件變量被通知
wait( std::unique_lock<std::mutex>)函數的實現方式是先原子解鎖線程,將當前線程加入到線程的等待執行列表,當notify_one或者notify_all解鎖阻塞線程之后,等待列表中的線程繼續執行完畢,并重新加鎖,最后退出wait()
調用此函數之前必須保證當前函數已經被鎖定,且這里使用的鎖需為unique_lock,因為只有unique_lock才能夠支持同一個線程的重復加解鎖。std::condition_variable::wait_for它的等待方式和wait的區別是增加了一個等待的時間,即除了等待notify_one解鎖之外還會有一個時間閾值,當到了這個時間閾值時仍然會退出。wait_for( std::unique_lock<std::mutex>& lock, const std::chrono::duration<Rep, Period>& rel_time)
根據以上條件變量的成員函數,修改代碼如下:
#include <iostream>
#include <thread>
#include <string>
#include <mutex>
#include <deque>
#include <condition_variable>
using namespace std;std::mutex mu;
std::deque<int> q;
std::condition_variable cond;
/*
線程1使用了 notify_one函數,來用作通知等待線程處理事務。
*/
void function_1(){int count = 100;while (count > 0) {std::unique_lock<std::mutex> locker(mu);q.push_back(count);locker.unlock();cond.notify_one();//如果環境中有多個等待線程,可以使用`notify_all()`同時觸發std::this_thread::sleep_for(std::chrono::seconds(1));count--;}
}
/*
線程2是等待線程,且為了防止偽激活,這里等待線程同時加入了 lamda表達式來標示當
全局隊列q也為空的時候線程2仍然處于等待狀態。否則偽激活又會像上一套代碼中的else
處理解鎖消耗系統資源。
*/
void function_2() {int data = 0;while (data != 1) {std::unique_lock<std::mutex> locker(mu);cond.wait(locker,[](){return !q.empty();});data=q.back();q.pop_back();locker.unlock();std::cout << "t2 got a value from t1 " << data << std::endl; }
}
int main()
{std::thread t1(function_1);std::thread t2(function_2);t1.join();t2.join();return 0;
}
輸出如下
t2 got a value from t1 100
t2 got a value from t1 99
t2 got a value from t1 98
t2 got a value from t1 97
t2 got a value from t1 96
t2 got a value from t1 95
...
...
參考文章:
https://en.cppreference.com/w/cpp/thread/condition_variable
https://zh.cppreference.com/w/cpp/thread/condition_variable/
總結
以上是生活随笔為你收集整理的C++ 多线程:条件变量 std::condition_variable的全部內容,希望文章能夠幫你解決所遇到的問題。