接著上節 atomic,本節主要介紹condition_varible的內容,練習代碼地址。本文參考http://www.cplusplus.com/reference/condition_variable/和http://en.cppreference.com/w/cpp/header/condition_variable學習。
一、 <condition_variable>頭文件結構
<condition_variable>頭文件主要包含了與條件變量相關的類和函數。主要含有類condition_variable和condition_variable_any,枚舉cv_status,函數notify_all_at_thread_exit。
Classes | description |
---|---|
condition_variable | 提供了std::unique_lock 相關聯的條件變量 |
condition_variable_any | 提供與任何鎖類型相關聯的條件變量 |
Enum classes | description |
---|---|
cv_status | 列出在條件變量上限時等待的可能結果(枚舉) |
Functions | description |
---|---|
notify_all_at_thread_exit | 當這個線程完全完成(函數)時,調度調用notify_all來調用 |
結構定義類似:
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;
- 條件變量類是一個同步原語,它可以用來阻塞一個線程或多個線程,直到另一個線程同時修改一個共享變量(條件),并通知條件變量。
- 條件變量是能夠阻塞調用線程的對象,直到通知恢復為止。
它使用unique_lock(在互斥鎖上)來鎖定線程,當它的一個等待函數被調用時。線程仍然被阻塞,直到被另一個線程喚醒,該線程調用同一個條件變量對象上的通知函數。 - 類型條件變量的對象總是使用unique_lock < mutex >等待:對于可以使用任何類型的可鎖定類型的選項,參見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]
* 構造一個condition_variable對象。
* 條件變量不能被復制/移動(這個類型的拷貝構造函數和賦值操作符都被刪除)。
* 如果構建失敗,將拋出system_error異常, 根據庫的實現,它可能會在其他情況下拋出異常。
exception type | error condition | description |
---|---|---|
system_error | errc::resource_unavailable_try_again | 資源限制(除了內存分配)防止初始化 |
-
(destructor)析構函數
* 破壞condition_variable對象。
* 在此條件下被阻塞的任何線程在調用此析構函數之前都將被通知。在這個析構函數被調用之后,沒有線程將開始等待。
注意:
1. 如果所有線程都被通知,調用析構函數是安全的。它們不需要退出各自的等待函數:一些線程可能還在等待重新獲得相關的鎖,或者等待調度在重新獲得它之后運行。
2.程序員必須確保在啟動析構函數時,沒有線程試圖等待*this,特別是當等待線程調用循環中的等待函數或使用執行謂詞的等待函數的重載時。
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、 當前線程的執行(該線程將鎖定lck的互斥鎖)將被阻塞,直到被通知。
2、 在阻塞線程的時刻,該函數將自動調用lck.unlock(),允許其他鎖定的線程繼續運行。
3、 一旦通知(顯式地,通過其他線程),函數就unblock并調用lck. lock(),在調用函數時將lck留在相同的狀態。然后函數返回(注意,最后一個互斥鎖可能會在返回之前阻塞線程)。
4、一般情況下,函數會被另一個線程的調用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實現可能產生虛假的喚醒調用,而不需要調用這些函數。因此,該函數的用戶將確保滿足恢復的條件。
5、 定義(2)如果指定pred,如果pred返回false,則函數只會阻塞,并且只有當它變為true時,通知才能解除線程(這對于檢查偽喚醒調用特別有用)。
類似這樣的實現: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、當前執行線程在rel_time時間內或者在被通知之前(該線程將鎖定lck的互斥鎖)被阻塞(如果后者先發生的話)。
2、在阻塞線程的時刻,該函數將自動調用lck.解鎖(),允許其他鎖定的線程繼續運行。
3、一旦通知或一次rel_time時間段已經過了,函數將會unblocks并調用lck. lock(),將lck與調用函數時的狀態保持一致。然后函數返回(注意,最后一個互斥鎖可能會在返回之前阻塞線程)。
4、一般情況下,函數會被另一個線程的調用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實現可能產生虛假的喚醒調用,而不需要調用這些函數。因此,該函數的用戶將確保滿足恢復的條件。
5、如果指定pred(定義2),如果pred返回false,則函數只會阻塞,并且只有當它變為true時,通知才能解除線程(這對于檢查偽喚醒調用特別有用)。它的行為好像是:
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、當前線程的執行(該線程將鎖定lck的互斥鎖)會被阻塞,直到被通知或到達abs_time時間點,無論那一個第一次發生。
2、在線程還處在阻塞時,該函數將自動調用lck.unlock(),允許其他鎖定的線程繼續運行。
3、一旦被通知或一旦abs_time時間到了,函數就會接觸阻塞狀態并調用lck. lock()。在調用函數時將lck與函數的狀態保持在同一狀態。然后函數返回(注意,最后一個互斥鎖可能會在返回之前阻塞線程)。
4、一般情況下,函數會被另一個線程的調用喚醒,無論是對成員notify_one還是成員notify_all。但是某些實現可能產生虛假的喚醒調用,而不需要調用這些函數。因此,該函數的使用者將要確保滿足恢復的條件。
5、(定義2)如果指定pred,如果pred返回false,則函數只會阻塞,并且只有當它變為true時,通知才能解除線程(這對于檢查偽喚醒調用特別有用)。它的行為好像是:
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、打開當前等待該條件的一個線程。如果沒有線程在等待,則該函數什么都不做。如果多于一個,則不指定哪個線程被選中。
2、該指令針對這個單獨的條件變量。這使得notify_one()不可能被延遲,并且在調用notify_one()之后才開始等待。
3、通知線程不需要將鎖保存在與等待線程所持有的相同互斥鎖上;事實上,這樣做是一個悲觀的方法,因為通知的線程會立即阻塞,等待通知線程釋放鎖。然而,一些實現(特別是許多pthreads實現)識別這種情況,并避免這種“匆忙而等待”的場景,通過將等待的線程從條件變量的隊列直接傳遞到通知調用的互斥鎖的隊列中,而不將其喚醒。
示例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; // 定義
解鎖當前等待該條件的所有線程。如果沒有線程在等待,則該函數什么都不做。
注意:
1、notify_one()/notify_all()和wait()/wait_for()/wait_until() 三個原子部分中的每一個影響都在一個完全的順序(解鎖+等待, 喚醒, 鎖)中,可以被視為一個原子變量的修改順序:該命令是特定于這個單獨的條件變量的。這使得notify_one()不可能被延遲,并且在調用notify_one()之后才開始等待。
2、通知線程不需要將鎖保存在與等待線程所持有的相同互斥鎖上;事實上,這樣做是一個悲觀的方法,因為通知的線程會立即阻塞,等待通知線程釋放鎖。
示例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、與條件變量一樣,除了它的等待函數可以將任何可鎖類型作為參數(條件變量對象只能使用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是一個標準布局類。它不可拷貝構造的,移動可構造的,不可拷貝復制和移動復制。
3、如果鎖是std::unique_lock,std::condition_variable可以提供更好的性能。
注意:
std::condition_variable_any與自定義的可鎖類型是用來提供方便的可中斷等待:自定義鎖操作會像預期的那樣鎖定相關的互斥對象,并且在接收到中斷信號時也執行必要的設置來通知這個條件變量。
1. Member functions
-
(constructor)構造函數
default (1) condition_variable_any();
copy [deleted] (2) condition_variable_any (const condition_variable_any&) = delete;
構造std::condition_variable_any類型的對象. 不能通過拷貝和移動構造(這個類型的拷貝構造函數和賦值操作符都被刪除)。
注意:
如果構造失敗,會拋出一個系統異常:
operator= [deleted] 不允許復制。
-
(destructor)析構函數
析構condition_variable_any對象。在此條件下被阻塞的任何線程在調用此析構函數之前都將被通知。在這個析構函數被調用之后,沒有線程將開始等待。
注意:
1、 如果所有線程都被通知,調用析構函數是安全的。它們不需要退出各自的等待函數:一些線程可能還在等待重新獲得相關的鎖,或者等待調度在重新獲得它之后運行。
2、 程序員必須確保在啟動析構函數時,沒有線程試圖等待,特別是當等待線程調用循環中的等待函數或使用執行謂詞的等待函數的重載時。
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、當前線程(當前正在鎖定lck)的執行將被阻止,直到通知為止。
2、在阻止線程的時刻,該函數自動調用lck.unlock(),允許其他鎖定線程繼續。
3、一旦通知(顯式地,通過某個其他線程),函數解除阻塞和調用lck.lock(),使lck處于與調用函數時相同的狀態。然后函數返回(注意,這個最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數被通知通過另一個線程的呼叫喚醒成員notify_one 或 notify_all。但是某些實現可能會產生虛假的喚醒呼叫,而不會調用這些功能。因此,該功能的用戶應確保其恢復條件得到滿足。
5、定義(2): 如果指定pred,如果pred返回false,則函數只會阻塞,并且只有當它變為true時,通知才能解除線程(這對于檢查偽喚醒調用特別有用)。表現為:
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、當前線程(當前鎖定lck)的執行在rel_time期間被阻塞,或者直到通知(如果后者首先發生)。
2、在阻止線程的時刻,該函數自動調用lck.unlock(),允許其他鎖定線程繼續。
3、一旦通知或一旦rel_time已經過去,該函數解除阻塞和調用lck.lock(),使lck處于與調用函數時相同的狀態。然后函數返回(注意,這個最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數被通知通過另一個線程的呼叫喚醒成員notify_one 或 notify_one。但是某些實現可能會產生虛假的喚醒呼叫,而不會調用這些功能。因此,該功能的用戶應確保其恢復條件得到滿足。
5、版本(2):如果指定pred,如果pred返回false,則函數只會阻塞,并且只有當它變為true時,通知才能解除線程(這對于檢查偽喚醒調用特別有用)。可以理解為
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、當前線程的執行(當前鎖定lck)將被阻塞,直到通知或直到abs_time,以先發生者為準。
2、在阻止線程的時刻,該函數自動調用lck.unlock(),允許其他鎖定線程繼續。
3、一旦通知或一旦為abs_time,函數解除阻塞和調用lck.lock(),使lck處于與調用函數時相同的狀態。然后函數返回(注意,這個最后的互斥鎖定可能在返回之前再次阻止線程)。
4、通常,該函數被通知通過另一個線程的呼叫喚醒成員notify_one 或會員 notify_one。但是某些實現可能會產生虛假的喚醒呼叫,而不會調用這些功能。因此,該功能的用戶應確保其恢復條件得到滿足。
5、版本2:如果指定pred,如果pred返回false,則函數只會阻塞,并且只有當它變為true時,通知才能解除線程(這對于檢查偽喚醒調用特別有用)。表現為:
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的函數。
unblock當前等待該條件的一個線程。如果沒有線程在等待,則該函數什么都不做。如果多于一個,則不指定哪個線程被選中。
示例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運行結果 1-10,在ubuntu14.04,會出現死鎖等待情況。
-
std::condition_variable_any::notify_all
同上面std::condition_variable的函數。打開當前等待該條件的所有線程。如果沒有線程在等待,則該函數什么都不做。
示例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; 定義
條件變量狀態:
表示是否由于超時而返回的函數。
這種類型的值由condition_variable和condition_variable_any的成員wait_for和wait_until返回。
定義類似:
enum class cv_status { no_timeout, timeout };
cv_status::no_timeout : 該函數返回沒有超時
cv_status::timeout : 該函數由于達到其時間限制(timeout)而返回。
四、Functions
1. std::notify_all_at_thread_exit
void notify_all_at_thread_exit (condition_variable& cond,unique_lock<mutex> lck);
當調用線程退出時,等待在cond上的所有線程都被通知恢復執行。
該函數還獲得由lck管理的mutex對象上的鎖的所有權,該對象在內部由函數存儲,并在線程退出時解鎖(僅在通知所有線程之前),行為如下:一旦所有具有線程存儲時間的對象都被銷毀:1 lck.unlock(); 2 cond.notify_all();
注意:
如果lock. mutex()沒有被當前線程鎖定,則調用此函數是未定義的行為。
如果lock. mutex()與當前在同一條件變量中等待的所有其他線程使用的互斥對象不相同,則調用此函數。
所提供的鎖一直保持到線程退出。一旦調用這個函數,就不會有更多的線程可能獲得相同的鎖以等待cond。如果某個線程在這個條件變量上等待,它不應該嘗試釋放并重新獲取鎖,當它被錯誤地喚醒時。
在典型的用例中,這個函數是由一個獨立的線程調用的最后一個函數。
示例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 編譯時報錯notify_all_at_thread_exit 不是 std的成語函數; 原因:/usr/include/c++/4.8/condition_variable文件中沒有對notify_all_at_thread_exit函數進行定義; vs2013中VC的include下的 condition_variable有定義,所以使用vs2013編譯運行正常。
到這里condition_varible 頭文件中的內容已經列完。 下一篇 c++11 多線程(5) future 總結