接著上節(jié) atomic,本節(jié)主要介紹condition_varible的內(nèi)容,練習(xí)代碼地址。本文參考http://www.cplusplus.com/reference/condition_variable/和http://en.cppreference.com/w/cpp/header/condition_variable學(xué)習(xí)。
一、 <condition_variable>頭文件結(jié)構(gòu)
<condition_variable>頭文件主要包含了與條件變量相關(guān)的類和函數(shù)。主要含有類condition_variable和condition_variable_any,枚舉cv_status,函數(shù)notify_all_at_thread_exit。
| Classes | description |
|---|---|
| condition_variable | 提供了std::unique_lock 相關(guān)聯(lián)的條件變量 |
| condition_variable_any | 提供與任何鎖類型相關(guān)聯(lián)的條件變量 |
| Enum classes | description |
|---|---|
| cv_status | 列出在條件變量上限時(shí)等待的可能結(jié)果(枚舉) |
| Functions | description |
|---|---|
| notify_all_at_thread_exit | 當(dāng)這個(gè)線程完全完成(函數(shù))時(shí),調(diào)度調(diào)用notify_all來調(diào)用 |
結(jié)構(gòu)定義類似:
namespace std {
class condition_variable;
class condition_variable_any;
void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
enum class cv_status {
no_timeout,
timeout
};
}
二、Classes
1. class condition_variable;
- 條件變量類是一個(gè)同步原語,它可以用來阻塞一個(gè)線程或多個(gè)線程,直到另一個(gè)線程同時(shí)修改一個(gè)共享變量(條件),并通知條件變量。
- 條件變量是能夠阻塞調(diào)用線程的對象,直到通知恢復(fù)為止。
它使用unique_lock(在互斥鎖上)來鎖定線程,當(dāng)它的一個(gè)等待函數(shù)被調(diào)用時(shí)。線程仍然被阻塞,直到被另一個(gè)線程喚醒,該線程調(diào)用同一個(gè)條件變量對象上的通知函數(shù)。 - 類型條件變量的對象總是使用unique_lock < mutex >等待:對于可以使用任何類型的可鎖定類型的選項(xiàng),參見condition_variable_any。
示例1:
// condition_variable example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::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);
ready = true;
cv.notify_all();
}
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";
go(); // go!
for (auto& th : threads) th.join();
return 0;
}
示例2:
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
return 0;
}
1. Member functions
condition_variable(); default (1)
condition_variable (const condition_variable&) = delete; copy [deleted] (2)
operator= [deleted]
* 構(gòu)造一個(gè)condition_variable對象。
* 條件變量不能被復(fù)制/移動(dòng)(這個(gè)類型的拷貝構(gòu)造函數(shù)和賦值操作符都被刪除)。
* 如果構(gòu)建失敗,將拋出system_error異常, 根據(jù)庫的實(shí)現(xiàn),它可能會(huì)在其他情況下拋出異常。
| exception type | error condition | description |
|---|---|---|
| system_error | errc::resource_unavailable_try_again | 資源限制(除了內(nèi)存分配)防止初始化 |
-
(destructor)析構(gòu)函數(shù)
* 破壞condition_variable對象。
* 在此條件下被阻塞的任何線程在調(diào)用此析構(gòu)函數(shù)之前都將被通知。在這個(gè)析構(gòu)函數(shù)被調(diào)用之后,沒有線程將開始等待。
注意:
1. 如果所有線程都被通知,調(diào)用析構(gòu)函數(shù)是安全的。它們不需要退出各自的等待函數(shù):一些線程可能還在等待重新獲得相關(guān)的鎖,或者等待調(diào)度在重新獲得它之后運(yùn)行。
2.程序員必須確保在啟動(dòng)析構(gòu)函數(shù)時(shí),沒有線程試圖等待*this,特別是當(dāng)?shù)却€程調(diào)用循環(huán)中的等待函數(shù)或使用執(zhí)行謂詞的等待函數(shù)的重載時(shí)。
2. Wait functions
-
std::condition_variable::wait
void wait (unique_lock<mutex>& lck); unconditional (1)
template <class Predicate> predicate (2)
void wait (unique_lock<mutex>& lck, Predicate pred);
1、 當(dāng)前線程的執(zhí)行(該線程將鎖定lck的互斥鎖)將被阻塞,直到被通知。
2、 在阻塞線程的時(shí)刻,該函數(shù)將自動(dòng)調(diào)用lck.unlock(),允許其他鎖定的線程繼續(xù)運(yùn)行。
3、 一旦通知(顯式地,通過其他線程),函數(shù)就unblock并調(diào)用lck. lock(),在調(diào)用函數(shù)時(shí)將lck留在相同的狀態(tài)。然后函數(shù)返回(注意,最后一個(gè)互斥鎖可能會(huì)在返回之前阻塞線程)。
4、一般情況下,函數(shù)會(huì)被另一個(gè)線程的調(diào)用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實(shí)現(xiàn)可能產(chǎn)生虛假的喚醒調(diào)用,而不需要調(diào)用這些函數(shù)。因此,該函數(shù)的用戶將確保滿足恢復(fù)的條件。
5、 定義(2)如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。
類似這樣的實(shí)現(xiàn):while (!pred()) wait(lck);
示例3:
// condition_variable::wait (with predicate)
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::yield
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
int cargo = 0;
bool shipment_available() {return cargo!=0;}
void consume (int n) {
for (int i=0; i<n; ++i) {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck,shipment_available);
// consume:
std::cout << cargo << '\n';
cargo=0;
}
}
int main ()
{
std::thread consumer_thread (consume,10);
// produce 10 items when needed:
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;
}
示例4:
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
std::condition_variable cv;
std::mutex cv_m; // This mutex is used for three purposes:
// 1) to synchronize accesses to i
// 2) to synchronize accesses to std::cerr
// 3) for the condition variable cv
int i = 0;
void waits()
{
std::unique_lock<std::mutex> lk(cv_m);
std::cerr << "Waiting... \n";
cv.wait(lk, []{return i == 1;});
std::cerr << "...finished waiting. i == 1\n";
}
void signals()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lk(cv_m);
std::cerr << "Notifying...\n";
}
cv.notify_all();
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lk(cv_m);
i = 1;
std::cerr << "Notifying again...\n";
}
cv.notify_all();
}
int main()
{
std::thread t1(waits), t2(waits), t3(waits), t4(signals);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
(1) unconditional
template <class Rep, class Period>
cv_status wait_for (unique_lock<mutex>& lck,
const chrono::duration<Rep,Period>& rel_time);
(2) predicate
template <class Rep, class Period, class Predicate>
bool wait_for (unique_lock<mutex>& lck,
const chrono::duration<Rep,Period>& rel_time, Predicate pred);
1、當(dāng)前執(zhí)行線程在rel_time時(shí)間內(nèi)或者在被通知之前(該線程將鎖定lck的互斥鎖)被阻塞(如果后者先發(fā)生的話)。
2、在阻塞線程的時(shí)刻,該函數(shù)將自動(dòng)調(diào)用lck.解鎖(),允許其他鎖定的線程繼續(xù)運(yùn)行。
3、一旦通知或一次rel_time時(shí)間段已經(jīng)過了,函數(shù)將會(huì)unblocks并調(diào)用lck. lock(),將lck與調(diào)用函數(shù)時(shí)的狀態(tài)保持一致。然后函數(shù)返回(注意,最后一個(gè)互斥鎖可能會(huì)在返回之前阻塞線程)。
4、一般情況下,函數(shù)會(huì)被另一個(gè)線程的調(diào)用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實(shí)現(xiàn)可能產(chǎn)生虛假的喚醒調(diào)用,而不需要調(diào)用這些函數(shù)。因此,該函數(shù)的用戶將確保滿足恢復(fù)的條件。
5、如果指定pred(定義2),如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。它的行為好像是:
return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));
示例5:
// condition_variable::wait_for example
#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_status
std::condition_variable cv;
int value;
void 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 (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::endl;
}
std::cout << "You entered: " << value << '\n';
th.join();
return 0;
}
示例6:
#include <iostream>
#include <atomic>
#include <condition_variable>
#include <thread>
#include <chrono>
//using namespace std::chrono_literals;
std::condition_variable cv;
std::mutex cv_m;
int i;
void waits(int idx)
{
std::unique_lock<std::mutex> lk(cv_m);
if(cv.wait_for(lk, idx*std::chrono::milliseconds(100), []{return i == 1;}))
std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n';
else
std::cerr << "Thread " << idx << " timed out. i == " << i << '\n';
}
void signals()
{
std::this_thread::sleep_for(std::chrono::milliseconds(120));
std::cerr << "Notifying...\n";
cv.notify_all();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
{
std::lock_guard<std::mutex> lk(cv_m);
i = 1;
}
std::cerr << "Notifying again...\n";
cv.notify_all();
}
int main()
{
std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals);
t1.join(); t2.join(), t3.join(), t4.join();
return 0;
}
(1) unconditional
template <class Clock, class Duration>
cv_status wait_until (unique_lock<mutex>& lck,
const chrono::time_point<Clock,Duration>& abs_time);
(2) predicate
template <class Clock, class Duration, class Predicate>
bool wait_until (unique_lock<mutex>& lck,
const chrono::time_point<Clock,Duration>& abs_time,
Predicate pred);
1、當(dāng)前線程的執(zhí)行(該線程將鎖定lck的互斥鎖)會(huì)被阻塞,直到被通知或到達(dá)abs_time時(shí)間點(diǎn),無論那一個(gè)第一次發(fā)生。
2、在線程還處在阻塞時(shí),該函數(shù)將自動(dòng)調(diào)用lck.unlock(),允許其他鎖定的線程繼續(xù)運(yùn)行。
3、一旦被通知或一旦abs_time時(shí)間到了,函數(shù)就會(huì)接觸阻塞狀態(tài)并調(diào)用lck. lock()。在調(diào)用函數(shù)時(shí)將lck與函數(shù)的狀態(tài)保持在同一狀態(tài)。然后函數(shù)返回(注意,最后一個(gè)互斥鎖可能會(huì)在返回之前阻塞線程)。
4、一般情況下,函數(shù)會(huì)被另一個(gè)線程的調(diào)用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實(shí)現(xiàn)可能產(chǎn)生虛假的喚醒調(diào)用,而不需要調(diào)用這些函數(shù)。因此,該函數(shù)的使用者將要確保滿足恢復(fù)的條件。
5、(定義2)如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。它的行為好像是:
while (!pred())
if ( wait_until(lck,abs_time) == cv_status::timeout)
return pred();
return true;
示例7:
#include <iostream>
#include <atomic>
#include <condition_variable>
#include <thread>
#include <chrono>
//using namespace std::chrono_literals;
std::condition_variable cv;
std::mutex cv_m;
std::atomic<int> i{0};
void waits(int idx)
{
std::unique_lock<std::mutex> lk(cv_m);
auto now = std::chrono::system_clock::now();
if(cv.wait_until(lk, now + idx*std::chrono::milliseconds(100), [](){return i == 1;}))
std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n';
else
std::cerr << "Thread " << idx << " timed out. i == " << i << '\n';
}
void signals()
{
std::this_thread::sleep_for(std::chrono::milliseconds(120));
std::cerr << "Notifying...\n";
cv.notify_all();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
i = 1;
std::cerr << "Notifying again...\n";
cv.notify_all();
}
int main()
{
std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
3. Notify functions
-
std::condition_variable::notify_one
void notify_one() noexcept; //定義
1、打開當(dāng)前等待該條件的一個(gè)線程。如果沒有線程在等待,則該函數(shù)什么都不做。如果多于一個(gè),則不指定哪個(gè)線程被選中。
2、該指令針對這個(gè)單獨(dú)的條件變量。這使得notify_one()不可能被延遲,并且在調(diào)用notify_one()之后才開始等待。
3、通知線程不需要將鎖保存在與等待線程所持有的相同互斥鎖上;事實(shí)上,這樣做是一個(gè)悲觀的方法,因?yàn)橥ㄖ木€程會(huì)立即阻塞,等待通知線程釋放鎖。然而,一些實(shí)現(xiàn)(特別是許多pthreads實(shí)現(xiàn))識別這種情況,并避免這種“匆忙而等待”的場景,通過將等待的線程從條件變量的隊(duì)列直接傳遞到通知調(diào)用的互斥鎖的隊(duì)列中,而不將其喚醒。
示例8:
// condition_variable::notify_one
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable produce,consume;
int cargo = 0; // shared value by producers and consumers
void consumer () {
std::unique_lock<std::mutex> lck(mtx);
while (cargo==0) consume.wait(lck);
std::cout << cargo << '\n';
cargo=0;
produce.notify_one();
}
void producer (int id) {
std::unique_lock<std::mutex> lck(mtx);
while (cargo!=0) produce.wait(lck);
cargo = id;
consume.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;
}
示例9:
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;
void waits()
{
std::unique_lock<std::mutex> lk(cv_m);
std::cout << "Waiting... \n";
cv.wait(lk, []{return i == 1;});
std::cout << "...finished waiting. i == 1\n";
done = true;
}
void signals()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Notifying falsely...\n";
cv.notify_one(); // waiting thread is notified with i == 0.
// cv.wait wakes up, checks i, and goes back to waiting
std::unique_lock<std::mutex> lk(cv_m);
i = 1;
while (!done)
{
std::cout << "Notifying true change...\n";
lk.unlock();
cv.notify_one(); // waiting thread is notified with i == 1, cv.wait returns
std::this_thread::sleep_for(std::chrono::seconds(1));
lk.lock();
}
}
int main()
{
std::thread t1(waits), t2(signals);
t1.join();
t2.join();
return 0;
}
-
std::condition_variable::notify_all
void notify_all() noexcept; // 定義
解鎖當(dāng)前等待該條件的所有線程。如果沒有線程在等待,則該函數(shù)什么都不做。
注意:
1、notify_one()/notify_all()和wait()/wait_for()/wait_until() 三個(gè)原子部分中的每一個(gè)影響都在一個(gè)完全的順序(解鎖+等待, 喚醒, 鎖)中,可以被視為一個(gè)原子變量的修改順序:該命令是特定于這個(gè)單獨(dú)的條件變量的。這使得notify_one()不可能被延遲,并且在調(diào)用notify_one()之后才開始等待。
2、通知線程不需要將鎖保存在與等待線程所持有的相同互斥鎖上;事實(shí)上,這樣做是一個(gè)悲觀的方法,因?yàn)橥ㄖ木€程會(huì)立即阻塞,等待通知線程釋放鎖。
示例10:
// condition_variable::notify_all
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::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);
ready = true;
cv.notify_all();
}
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";
go(); // go!
for (auto& th : threads) th.join();
return 0;
}
示例11:
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
std::condition_variable cv;
std::mutex cv_m; // This mutex is used for three purposes:
// 1) to synchronize accesses to i
// 2) to synchronize accesses to std::cerr
// 3) for the condition variable cv
int i = 0;
void waits()
{
std::unique_lock<std::mutex> lk(cv_m);
std::cerr << "Waiting... \n";
cv.wait(lk, []{return i == 1;});
std::cerr << "...finished waiting. i == 1\n";
}
void signals()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lk(cv_m);
std::cerr << "Notifying...\n";
}
cv.notify_all();
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lk(cv_m);
i = 1;
std::cerr << "Notifying again...\n";
}
cv.notify_all();
}
int main()
{
std::thread t1(waits), t2(waits), t3(waits), t4(signals);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
2. class condition_variable_any;
1、與條件變量一樣,除了它的等待函數(shù)可以將任何可鎖類型作為參數(shù)(條件變量對象只能使用unique_lock < mutex >)。除此之外,它們是相同的。
2、std::condition_variable_any是std::condition_variable的泛化。而std::condition_variable 只能用于std::unique_lock < std::mutex >,condition_variable_any可以操作任何滿足基本要求的鎖。參見std::condition_變量,用于描述條件變量的語義。類std::condition_variable_any是一個(gè)標(biāo)準(zhǔn)布局類。它不可拷貝構(gòu)造的,移動(dòng)可構(gòu)造的,不可拷貝復(fù)制和移動(dòng)復(fù)制。
3、如果鎖是std::unique_lock,std::condition_variable可以提供更好的性能。
注意:
std::condition_variable_any與自定義的可鎖類型是用來提供方便的可中斷等待:自定義鎖操作會(huì)像預(yù)期的那樣鎖定相關(guān)的互斥對象,并且在接收到中斷信號時(shí)也執(zhí)行必要的設(shè)置來通知這個(gè)條件變量。
1. Member functions
-
(constructor)構(gòu)造函數(shù)
default (1) condition_variable_any();
copy [deleted] (2) condition_variable_any (const condition_variable_any&) = delete;
構(gòu)造std::condition_variable_any類型的對象. 不能通過拷貝和移動(dòng)構(gòu)造(這個(gè)類型的拷貝構(gòu)造函數(shù)和賦值操作符都被刪除)。
注意:
如果構(gòu)造失敗,會(huì)拋出一個(gè)系統(tǒng)異常:

operator= [deleted] 不允許復(fù)制。
-
(destructor)析構(gòu)函數(shù)
析構(gòu)condition_variable_any對象。在此條件下被阻塞的任何線程在調(diào)用此析構(gòu)函數(shù)之前都將被通知。在這個(gè)析構(gòu)函數(shù)被調(diào)用之后,沒有線程將開始等待。
注意:
1、 如果所有線程都被通知,調(diào)用析構(gòu)函數(shù)是安全的。它們不需要退出各自的等待函數(shù):一些線程可能還在等待重新獲得相關(guān)的鎖,或者等待調(diào)度在重新獲得它之后運(yùn)行。
2、 程序員必須確保在啟動(dòng)析構(gòu)函數(shù)時(shí),沒有線程試圖等待,特別是當(dāng)?shù)却€程調(diào)用循環(huán)中的等待函數(shù)或使用執(zhí)行謂詞的等待函數(shù)的重載時(shí)。
2. Wait functions
- [std::condition_variable_any::wait]
(http://www.cplusplus.com/reference/condition_variable/condition_variable_any/wait/)
unconditional (1) template <class Lock> void wait (Lock& lck);
predicate (2) template <class Lock, class Predicate>
void wait (Lock& lck, Predicate pred);
1、當(dāng)前線程(當(dāng)前正在鎖定lck)的執(zhí)行將被阻止,直到通知為止。
2、在阻止線程的時(shí)刻,該函數(shù)自動(dòng)調(diào)用lck.unlock(),允許其他鎖定線程繼續(xù)。
3、一旦通知(顯式地,通過某個(gè)其他線程),函數(shù)解除阻塞和調(diào)用lck.lock(),使lck處于與調(diào)用函數(shù)時(shí)相同的狀態(tài)。然后函數(shù)返回(注意,這個(gè)最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數(shù)被通知通過另一個(gè)線程的呼叫喚醒成員notify_one 或 notify_all。但是某些實(shí)現(xiàn)可能會(huì)產(chǎn)生虛假的喚醒呼叫,而不會(huì)調(diào)用這些功能。因此,該功能的用戶應(yīng)確保其恢復(fù)條件得到滿足。
5、定義(2): 如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。表現(xiàn)為:
while (! pred ()) {
wait ( lock );
}
示例12:
// condition_variable_any::wait (with predicate)
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::yield
#include <mutex> // std::mutex
#include <condition_variable> // std::condition_variable_any
std::mutex mtx;
std::condition_variable_any cv;
int cargo = 0;
bool shipment_available() {return cargo!=0;}
void consume (int n) {
for (int i=0; i<n; ++i) {
mtx.lock();
cv.wait(mtx,shipment_available);
// consume:
std::cout << cargo << '\n';
cargo=0;
mtx.unlock();
}
}
int main ()
{
std::thread consumer_thread (consume,10);
// produce 10 items when needed:
for (int i=0; i<10; ++i) {
while (shipment_available()) std::this_thread::yield();
mtx.lock();
cargo = i+1;
cv.notify_one();
mtx.unlock();
}
consumer_thread.join();
return 0;
}
2.std::condition_variable_any::wait_for
unconditional (1)
template <class Lock, class Rep, class Period>
cv_status wait_for (Lock& lck,
const chrono::duration<Rep,Period>& rel_time);
predicate (2)
template <class Lock, class Rep, class Period, class Predicate>
bool wait_for (Lock& lck,
const chrono::duration<Rep,Period>& rel_time, Predicate pred);
1、當(dāng)前線程(當(dāng)前鎖定lck)的執(zhí)行在rel_time期間被阻塞,或者直到通知(如果后者首先發(fā)生)。
2、在阻止線程的時(shí)刻,該函數(shù)自動(dòng)調(diào)用lck.unlock(),允許其他鎖定線程繼續(xù)。
3、一旦通知或一旦rel_time已經(jīng)過去,該函數(shù)解除阻塞和調(diào)用lck.lock(),使lck處于與調(diào)用函數(shù)時(shí)相同的狀態(tài)。然后函數(shù)返回(注意,這個(gè)最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數(shù)被通知通過另一個(gè)線程的呼叫喚醒成員notify_one 或 notify_one。但是某些實(shí)現(xiàn)可能會(huì)產(chǎn)生虛假的喚醒呼叫,而不會(huì)調(diào)用這些功能。因此,該功能的用戶應(yīng)確保其恢復(fù)條件得到滿足。
5、版本(2):如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)??梢岳斫鉃?/p>
return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));
示例13:
// condition_variable_any::wait_for example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <chrono> // std::chrono::seconds
#include <mutex> // std::mutex
#include <condition_variable> // std::condition_variable_any, std::cv_status
std::condition_variable_any cv;
int value;
void read_value() {
std::cin >> value;
cv.notify_one();
}
int main ()
{
std::cout << "Please, enter an integer (I'll be printing dots): ";
std::thread th (read_value);
std::mutex mtx;
mtx.lock();
while (cv.wait_for(mtx,std::chrono::seconds(1))==std::cv_status::timeout) {
std::cout << '.';
}
std::cout << "You entered: " << value << '\n';
mtx.unlock();
th.join();
return 0;
}
unconditional (1)
template <class Lock, class Clock, class Duration>
cv_status wait_until (Lock& lck,
const chrono::time_point<Clock,Duration>& abs_time);
predicate (2)
template <class Lock, class Clock, class Duration, class Predicate>
bool wait_until (Lock& lck,
const chrono::time_point<Clock,Duration>& abs_time,
Predicate pred);
1、當(dāng)前線程的執(zhí)行(當(dāng)前鎖定lck)將被阻塞,直到通知或直到abs_time,以先發(fā)生者為準(zhǔn)。
2、在阻止線程的時(shí)刻,該函數(shù)自動(dòng)調(diào)用lck.unlock(),允許其他鎖定線程繼續(xù)。
3、一旦通知或一旦為abs_time,函數(shù)解除阻塞和調(diào)用lck.lock(),使lck處于與調(diào)用函數(shù)時(shí)相同的狀態(tài)。然后函數(shù)返回(注意,這個(gè)最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數(shù)被通知通過另一個(gè)線程的呼叫喚醒成員notify_one 或會(huì)員 notify_one。但是某些實(shí)現(xiàn)可能會(huì)產(chǎn)生虛假的喚醒呼叫,而不會(huì)調(diào)用這些功能。因此,該功能的用戶應(yīng)確保其恢復(fù)條件得到滿足。
5、版本2:如果指定pred,如果pred返回false,則函數(shù)只會(huì)阻塞,并且只有當(dāng)它變?yōu)閠rue時(shí),通知才能解除線程(這對于檢查偽喚醒調(diào)用特別有用)。表現(xiàn)為:
while (!pred())
if ( wait_until(lck,abs_time) == cv_status::timeout)
return pred();
return true;
示例14:
#include <iostream>
#include <atomic>
#include <condition_variable>
#include <thread>
#include <chrono>
//using namespace std::chrono_literals;
std::condition_variable cv;
std::mutex cv_m;
std::atomic<int> i{0};
void waits(int idx) {
std::unique_lock<std::mutex> lk(cv_m);
auto now = std::chrono::system_clock::now();
if(cv.wait_until(lk, now + idx*std::chrono::milliseconds(100), [](){return i == 1;}))
std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n';
else
std::cerr << "Thread " << idx << " timed out. i == " << i << '\n';
}
void signals() {
std::this_thread::sleep_for(std::chrono::milliseconds(120));
std::cerr << "Notifying...\n";
cv.notify_all();
std::this_thread::sleep_for(std::chrono::milliseconds(100));
i = 1;
std::cerr << "Notifying again...\n";
cv.notify_all();
}
int main() {
std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
3. Notify functions
-
std::condition_variable_any::notify_one
同上面std::condition_variable的函數(shù)。
unblock當(dāng)前等待該條件的一個(gè)線程。如果沒有線程在等待,則該函數(shù)什么都不做。如果多于一個(gè),則不指定哪個(gè)線程被選中。
示例15:
// condition_variable_any::notify_one
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
#include <condition_variable> // std::condition_variable_any
std::mutex mtx;
std::condition_variable_any cv;
int cargo = 0; // shared value by producers and consumers
void consumer () {
mtx.lock();
while (cargo==0) cv.wait(mtx);
std::cout << cargo << '\n';
cargo=0;
mtx.unlock();
}
void producer (int id) {
mtx.lock();
cargo = id;
cv.notify_one();
mtx.unlock();
}
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;
}
本例子在vs2013運(yùn)行結(jié)果 1-10,在ubuntu14.04,會(huì)出現(xiàn)死鎖等待情況。
-
std::condition_variable_any::notify_all
同上面std::condition_variable的函數(shù)。打開當(dāng)前等待該條件的所有線程。如果沒有線程在等待,則該函數(shù)什么都不做。
示例16:
// condition_variable_any::notify_all
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
#include <condition_variable> // std::condition_variable_any
std::mutex mtx;
std::condition_variable_any cv;
bool ready = false;
void print_id (int id) {
mtx.lock();
while (!ready) cv.wait(mtx);
// ...
std::cout << "thread " << id << '\n';
mtx.unlock();
}
void go() {
mtx.lock();
ready = true;
cv.notify_all();
mtx.unlock();
}
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";
go(); // go!
for (auto& th : threads) th.join();
return 0;
}
三、 Enum classes
1. std::cv_status
enum class cv_status; 定義
條件變量狀態(tài):
表示是否由于超時(shí)而返回的函數(shù)。
這種類型的值由condition_variable和condition_variable_any的成員wait_for和wait_until返回。
定義類似:
enum class cv_status { no_timeout, timeout };
cv_status::no_timeout : 該函數(shù)返回沒有超時(shí)
cv_status::timeout : 該函數(shù)由于達(dá)到其時(shí)間限制(timeout)而返回。
四、Functions
1. std::notify_all_at_thread_exit
void notify_all_at_thread_exit (condition_variable& cond,unique_lock<mutex> lck);
當(dāng)調(diào)用線程退出時(shí),等待在cond上的所有線程都被通知恢復(fù)執(zhí)行。
該函數(shù)還獲得由lck管理的mutex對象上的鎖的所有權(quán),該對象在內(nèi)部由函數(shù)存儲(chǔ),并在線程退出時(shí)解鎖(僅在通知所有線程之前),行為如下:一旦所有具有線程存儲(chǔ)時(shí)間的對象都被銷毀:1 lck.unlock(); 2 cond.notify_all();
注意:
如果lock. mutex()沒有被當(dāng)前線程鎖定,則調(diào)用此函數(shù)是未定義的行為。
如果lock. mutex()與當(dāng)前在同一條件變量中等待的所有其他線程使用的互斥對象不相同,則調(diào)用此函數(shù)。
所提供的鎖一直保持到線程退出。一旦調(diào)用這個(gè)函數(shù),就不會(huì)有更多的線程可能獲得相同的鎖以等待cond。如果某個(gè)線程在這個(gè)條件變量上等待,它不應(yīng)該嘗試釋放并重新獲取鎖,當(dāng)它被錯(cuò)誤地喚醒時(shí)。
在典型的用例中,這個(gè)函數(shù)是由一個(gè)獨(dú)立的線程調(diào)用的最后一個(gè)函數(shù)。
示例17:
// notify_all_at_thread_exit
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::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;
}
上面的例子ubuntu14.04 g++4.8.4 編譯時(shí)報(bào)錯(cuò)notify_all_at_thread_exit 不是 std的成語函數(shù); 原因:/usr/include/c++/4.8/condition_variable文件中沒有對notify_all_at_thread_exit函數(shù)進(jìn)行定義; vs2013中VC的include下的 condition_variable有定義,所以使用vs2013編譯運(yùn)行正常。
到這里condition_varible 頭文件中的內(nèi)容已經(jīng)列完。 下一篇 c++11 多線程(5) future 總結(jié)