【转】C++11 并发指南五(std::condition_variable 详解)
http://www.cnblogs.com/haippy/p/3252041.html
前面三講《C++11 并發(fā)指南二(std::thread 詳解)》,《C++11 并發(fā)指南三(std::mutex 詳解)》分別介紹了 std::thread,std::mutex,std::future 等相關(guān)內(nèi)容,相信讀者對(duì) C++11 中的多線程編程有了一個(gè)最基本的認(rèn)識(shí),本文將介紹 C++11 標(biāo)準(zhǔn)中 <condition_variable> 頭文件里面的類和相關(guān)函數(shù)。
<condition_variable > 頭文件主要包含了與條件變量相關(guān)的類和函數(shù)。相關(guān)的類包括 std::condition_variable 和 std::condition_variable_any,還有枚舉類型std::cv_status。另外還包括函數(shù) std::notify_all_at_thread_exit(),下面分別介紹一下以上幾種類型。
std::condition_variable 類介紹
std::condition_variable 是條件變量,更多有關(guān)條件變量的定義參考維基百科。Linux 下使用 Pthread 庫中的 pthread_cond_*() 函數(shù)提供了與條件變量相關(guān)的功能, Windows 則參考MSDN。
當(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)前線程。
std::condition_variable 對(duì)象通常使用 std::unique_lock<std::mutex> 來等待,如果需要使用另外的 lockable 類型,可以使用 std::condition_variable_any 類,本文后面會(huì)講到 std::condition_variable_any 的用法。
首先我們來看一個(gè)簡單的例子
#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variablestd::mutex mtx; // 全局互斥鎖. std::condition_variable cv; // 全局條件變量. bool ready = false; // 全局標(biāo)志位.void do_print_id(int id) {std::unique_lock <std::mutex> lck(mtx);while (!ready) // 如果標(biāo)志位不為 true, 則等待...cv.wait(lck); // 當(dāng)前線程被阻塞, 當(dāng)全局標(biāo)志位變?yōu)?true 之后,// 線程被喚醒, 繼續(xù)往下執(zhí)行打印線程編號(hào)id.std::cout << "thread " << id << '\n'; }void go() {std::unique_lock <std::mutex> lck(mtx);ready = true; // 設(shè)置全局標(biāo)志位為 true.cv.notify_all(); // 喚醒所有線程. }int main() {std::thread threads[10];// spawn 10 threads:for (int i = 0; i < 10; ++i)threads[i] = std::thread(do_print_id, i);std::cout << "10 threads ready to race...\n";go(); // go!for (auto & th:threads)th.join();return 0; }執(zhí)行結(jié)果如下:
concurrency ) ./ConditionVariable-basic1 10 threads ready to race... thread 1 thread 0 thread 2 thread 3 thread 4 thread 5 thread 6 thread 7 thread 8 thread 9好了,對(duì)條件變量有了一個(gè)基本的了解之后,我們來看看 std::condition_variable 的各個(gè)成員函數(shù)。
std::condition_variable 構(gòu)造函數(shù)
| condition_variable(); |
| condition_variable (const condition_variable&) = delete; |
std::condition_variable 的拷貝構(gòu)造函數(shù)被禁用,只提供了默認(rèn)構(gòu)造函數(shù)。
std::condition_variable::wait() 介紹
| void wait (unique_lock<mutex>& lck); |
| template <class Predicate>void wait (unique_lock<mutex>& lck, Predicate pred); |
std::condition_variable 提供了兩種 wait() 函數(shù)。當(dāng)前線程調(diào)用 wait() 后將被阻塞(此時(shí)當(dāng)前線程應(yīng)該獲得了鎖(mutex),不妨設(shè)獲得鎖 lck),直到另外某個(gè)線程調(diào)用 notify_* 喚醒了當(dāng)前線程。
在線程被阻塞時(shí),該函數(shù)會(huì)自動(dòng)調(diào)用?lck.unlock() 釋放鎖,使得其他被阻塞在鎖競爭上的線程得以繼續(xù)執(zhí)行。另外,一旦當(dāng)前線程獲得通知(notified,通常是另外某個(gè)線程調(diào)用 notify_* 喚醒了當(dāng)前線程),wait() 函數(shù)也是自動(dòng)調(diào)用 lck.lock(),使得 lck 的狀態(tài)和 wait 函數(shù)被調(diào)用時(shí)相同。
在第二種情況下(即設(shè)置了 Predicate),只有當(dāng) pred 條件為 false 時(shí)調(diào)用 wait() 才會(huì)阻塞當(dāng)前線程,并且在收到其他線程的通知后只有當(dāng) pred 為 true 時(shí)才會(huì)被解除阻塞。因此第二種情況類似以下代碼:
請(qǐng)看下面例子(參考):
#include <iostream> // std::cout #include <thread> // std::thread, std::this_thread::yield #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variablestd::mutex mtx; std::condition_variable cv;int cargo = 0; bool shipment_available() {return cargo != 0; }// 消費(fèi)者線程. void consume(int n) {for (int i = 0; i < n; ++i) {std::unique_lock <std::mutex> lck(mtx);cv.wait(lck, shipment_available);std::cout << cargo << '\n';cargo = 0;} }int main() {std::thread consumer_thread(consume, 10); // 消費(fèi)者線程.// 主線程為生產(chǎn)者線程, 生產(chǎn) 10 個(gè)物品.for (int i = 0; i < 10; ++i) {while (shipment_available())std::this_thread::yield();std::unique_lock <std::mutex> lck(mtx);cargo = i + 1;cv.notify_one();}consumer_thread.join();return 0; }程序執(zhí)行結(jié)果如下:
concurrency ) ./ConditionVariable-wait 1 2 3 4 5 6 7 8 9 10std::condition_variable::wait_for() 介紹
| template <class Rep, class Period>cv_status wait_for (unique_lock<mutex>& lck,const chrono::duration<Rep,Period>& rel_time); |
| template <class Rep, class Period, class Predicate>bool wait_for (unique_lock<mutex>& lck,const chrono::duration<Rep,Period>& rel_time, Predicate pred); |
與?std::condition_variable::wait()?類似,不過 wait_for 可以指定一個(gè)時(shí)間段,在當(dāng)前線程收到通知或者指定的時(shí)間 rel_time 超時(shí)之前,該線程都會(huì)處于阻塞狀態(tài)。而一旦超時(shí)或者收到了其他線程的通知,wait_for 返回,剩下的處理步驟和 wait() 類似。
另外,wait_for 的重載版本(predicte(2))的最后一個(gè)參數(shù) pred 表示 wait_for 的預(yù)測條件,只有當(dāng) pred 條件為 false 時(shí)調(diào)用 wait() 才會(huì)阻塞當(dāng)前線程,并且在收到其他線程的通知后只有當(dāng) pred 為 true 時(shí)才會(huì)被解除阻塞,因此相當(dāng)于如下代碼:
return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));請(qǐng)看下面的例子(參考),下面的例子中,主線程等待 th 線程輸入一個(gè)值,然后將 th 線程從終端接收的值打印出來,在 th 線程接受到值之前,主線程一直等待,每個(gè)一秒超時(shí)一次,并打印一個(gè) ".":
#include <iostream> // std::cout #include <thread> // std::thread #include <chrono> // std::chrono::seconds #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variable, std::cv_statusstd::condition_variable cv;int value;void do_read_value() {std::cin >> value;cv.notify_one(); }int main () {std::cout << "Please, enter an integer (I'll be printing dots): \n";std::thread th(do_read_value);std::mutex mtx;std::unique_lock<std::mutex> lck(mtx);while (cv.wait_for(lck,std::chrono::seconds(1)) == std::cv_status::timeout) {std::cout << '.';std::cout.flush();}std::cout << "You entered: " << value << '\n';th.join();return 0; }std::condition_variable::wait_until 介紹
| template <class Clock, class Duration>cv_status wait_until (unique_lock<mutex>& lck,const chrono::time_point<Clock,Duration>& abs_time); |
| template <class Clock, class Duration, class Predicate>bool wait_until (unique_lock<mutex>& lck,const chrono::time_point<Clock,Duration>& abs_time,Predicate pred); |
與?std::condition_variable::wait_for?類似,但是 wait_until 可以指定一個(gè)時(shí)間點(diǎn),在當(dāng)前線程收到通知或者指定的時(shí)間點(diǎn) abs_time 超時(shí)之前,該線程都會(huì)處于阻塞狀態(tài)。而一旦超時(shí)或者收到了其他線程的通知,wait_until 返回,剩下的處理步驟和 wait_until() 類似。
另外,wait_until 的重載版本(predicte(2))的最后一個(gè)參數(shù) pred 表示?wait_until 的預(yù)測條件,只有當(dāng) pred 條件為 false 時(shí)調(diào)用 wait() 才會(huì)阻塞當(dāng)前線程,并且在收到其他線程的通知后只有當(dāng) pred 為 true 時(shí)才會(huì)被解除阻塞,因此相當(dāng)于如下代碼:
while (!pred())if ( wait_until(lck,abs_time) == cv_status::timeout)return pred(); return true;std::condition_variable::notify_one() 介紹
喚醒某個(gè)等待(wait)線程。如果當(dāng)前沒有等待線程,則該函數(shù)什么也不做,如果同時(shí)存在多個(gè)等待線程,則喚醒某個(gè)線程是不確定的(unspecified)。
請(qǐng)看下例(參考):
#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variablestd::mutex mtx; std::condition_variable cv;int cargo = 0; // shared value by producers and consumersvoid consumer() {std::unique_lock < std::mutex > lck(mtx);while (cargo == 0)cv.wait(lck);std::cout << cargo << '\n';cargo = 0; }void producer(int id) {std::unique_lock < std::mutex > lck(mtx);cargo = id;cv.notify_one(); }int main() {std::thread consumers[10], producers[10];// spawn 10 consumers and 10 producers:for (int i = 0; i < 10; ++i) {consumers[i] = std::thread(consumer);producers[i] = std::thread(producer, i + 1);}// join them back:for (int i = 0; i < 10; ++i) {producers[i].join();consumers[i].join();}return 0; }std::condition_variable::notify_all() 介紹
喚醒所有的等待(wait)線程。如果當(dāng)前沒有等待線程,則該函數(shù)什么也不做。請(qǐng)看下面的例子:
#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variablestd::mutex mtx; // 全局互斥鎖. std::condition_variable cv; // 全局條件變量. bool ready = false; // 全局標(biāo)志位.void do_print_id(int id) {std::unique_lock <std::mutex> lck(mtx);while (!ready) // 如果標(biāo)志位不為 true, 則等待...cv.wait(lck); // 當(dāng)前線程被阻塞, 當(dāng)全局標(biāo)志位變?yōu)?true 之后,// 線程被喚醒, 繼續(xù)往下執(zhí)行打印線程編號(hào)id.std::cout << "thread " << id << '\n'; }void go() {std::unique_lock <std::mutex> lck(mtx);ready = true; // 設(shè)置全局標(biāo)志位為 true.cv.notify_all(); // 喚醒所有線程. }int main() {std::thread threads[10];// spawn 10 threads:for (int i = 0; i < 10; ++i)threads[i] = std::thread(do_print_id, i);std::cout << "10 threads ready to race...\n";go(); // go!for (auto & th:threads)th.join();return 0; }?std::condition_variable_any 介紹
與 std::condition_variable 類似,只不過 std::condition_variable_any 的 wait 函數(shù)可以接受任何 lockable 參數(shù),而 std::condition_variable 只能接受 std::unique_lock<std::mutex> 類型的參數(shù),除此以外,和 std::condition_variable 幾乎完全一樣。
std::cv_status 枚舉類型介紹
| cv_status::no_timeout | wait_for 或者 wait_until 沒有超時(shí),即在規(guī)定的時(shí)間段內(nèi)線程收到了通知。 |
| cv_status::timeout | wait_for 或者 wait_until 超時(shí)。 |
std::notify_all_at_thread_exit
函數(shù)原型為:
void notify_all_at_thread_exit (condition_variable& cond, unique_lock<mutex> lck);當(dāng)調(diào)用該函數(shù)的線程退出時(shí),所有在?cond?條件變量上等待的線程都會(huì)收到通知。請(qǐng)看下例(參考):
#include <iostream> // std::cout #include <thread> // std::thread #include <mutex> // std::mutex, std::unique_lock #include <condition_variable> // std::condition_variablestd::mutex mtx; std::condition_variable cv; bool ready = false;void print_id (int id) {std::unique_lock<std::mutex> lck(mtx);while (!ready) cv.wait(lck);// ...std::cout << "thread " << id << '\n'; }void go() {std::unique_lock<std::mutex> lck(mtx);std::notify_all_at_thread_exit(cv,std::move(lck));ready = true; }int main () {std::thread threads[10];// spawn 10 threads:for (int i=0; i<10; ++i)threads[i] = std::thread(print_id,i);std::cout << "10 threads ready to race...\n";std::thread(go).detach(); // go!for (auto& th : threads) th.join();return 0; }?
好了,到此為止,<condition_variable> 頭文件中的兩個(gè)條件變量類(std::condition_variable 和 std::condition_variable_any)、枚舉類型(std::cv_status)、以及輔助函數(shù)(std::notify_all_at_thread_exit())都已經(jīng)介紹完了。從下一章開始我會(huì)逐步開始介紹 <atomic> 頭文件中的內(nèi)容,后續(xù)的文章還會(huì)介紹 C++11 的內(nèi)存模型,涉及內(nèi)容稍微底層一些,希望大家能夠保持興趣,學(xué)完 C++11 并發(fā)編程,如果你發(fā)現(xiàn)本文中的錯(cuò)誤,也請(qǐng)給我反饋 ;-)。
轉(zhuǎn)載于:https://www.cnblogs.com/fvsfvs123/p/4446959.html
總結(jié)
以上是生活随笔為你收集整理的【转】C++11 并发指南五(std::condition_variable 详解)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Apache + PHP 服务
- 下一篇: 在Emacs中使用ECB(转载)