C++多線程之條件變量 - condition_variable
condition_variable是一個能夠堵塞調用線程直到被通知恢復的對象。
當調用condition_variable的某個等待函數時,它使用unique_lock來鎖定線程。該線程會保持堵塞狀態,直到被另一個線程通過調用同一個condition_variable對象的通知函數來喚醒為止。
1.wait(...)
void wait(unique_lock<mutex>& lck);
template<class Predicate>
void wait(unique_lock<mutex>& lck, Predicate pred())
調用wait函數后,當前線程的執行被堵塞,直到得到通知。
再阻塞線程時,該函數為自動調用lck.unlock(),從而允許其它被鎖定的線程能繼續執行。
當收到其它線程的通知時,該函數將取消阻塞並調用lck.lock(),使lck處於調用該函數時相同的狀態,然后函數返回(注意,最后的互斥鎖可能會在返回之前再次阻塞線程)。
通常通過另一個線程調用notify_one或notify_all來通知該函數喚醒,但是某些實現可能會產生虛假的調用喚醒,而不會調用這些函數中的任何一個,因此使用此功能時應該確保滿足其恢復條件,我們應該保證總是在循環中使用普通的wait函數。例如:while(...) cv.wait()
如果指定了pred,則該函數僅再pred返回false時菜阻塞,並且通知只能在線程變為true時才能取消阻塞線程,這對檢查虛假喚醒調用特別有用。等同於:while(!pred()) wait(lck)
另外需要特別注意的是在銷毀一個condition_variable時必須通知所有處於等待狀態的線程,否則它們可能會一直等待下去。
實例:
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
std::mutex gMutex;
std::condition_variable gCv;
int gCargo;
bool sufficient()
{
return 0 != gCargo;
}
void consume(int n)
{
for(int i = 0; i < n; ++i)
{
std::unique_lock<std::mutex> lck(gMutex);
gCv.wait(lck, sufficient);
std::cout << "cargo: " << gCargo << std::endl;
gCargo = 0;
}
}
int main()
{
std::thread consume_tread(consume, 10);
for(int i = 0; i < 10; ++i)
{
while(sufficient())
{
std::this_thread::yield();
}
std::unique_lock<std::mutex> lck(gMutex);
gCargo = i + 1;
gCv.notify_one();
}
consume_tread.join();
return 0;
}
2.wait_until(...)
template <class Clock, class Duration>
cv_status wait_until (unique_lock<mutex>& lck,
const chrono::time_point<Clock,Duration>& abs_time);
當前線程的執行被阻塞,直到被通知或直到abs_time為止(以先發生者為准)。
在阻塞線程時,該函數會自動調用lck.unlock(),從而允許其他鎖定的線程繼續執行。
收到通知或到達abs_time后,該函數將取消阻塞並調用lck.lock(),使lck處於與調用該函數相同的狀態。然后函數返回(注意,最后的互斥鎖可能會在返回之前再次阻塞線程)。
template <class Clock, class Duration, class Predicate>
bool wait_until (unique_lock<mutex>& lck,
const chrono::time_point<Clock,Duration>& abs_time,
Predicate pred);
如果指定了pred,等同於:
while (!pred())
if ( wait_until(lck,abs_time) == cv_status::timeout)
return pred();
3.wait_for(...)
template<class Rep, class Period>
cv_status wait_for(unique_lock<mutex>& lck,
const chrono::duration<Rep, Period>& rel_time);
等同於:
cv.wait_until(lck, steady_clock::now + rel_time)
示例:
// 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;
}
template<class Rep, class Period, class Predicate>
bool wait_for(unique_lock<mutex>* lck,
const chrono::duration<Rep, Period>& rel_time,
Predicate pred);
等同於:
cv.wait_until(lck, steady_clock::now() + rel_time, move(pred))
4.cv_status
enum class cv_status { no_timeout, timeout };
5.notify_one
void notify_one() noexcept;
從正在等待該條件變量的所有線程中解除一個線程的阻塞。如果沒有線程在等待,則該函數不執行任何操作。如果有多個線程在等待它也不會指定解除哪個線程的阻塞狀態。
6.notify_all
void notify_all() noexcept;
解除所有正在等待該條件變量線程的阻塞狀態。如果沒有線程在等待,則該函數不執行任何操作。
參考鏈接鏈接