<condition_variable > 头文件主要包含了与条件变量相关的类和函数,如下:
condition_variable (C++11) | ||||
(C++11) | ||||
(C++11) | ||||
(C++11) |
std::condition_variable
condition_variable
类是同步原语,能用于阻塞一个线程,或同时阻塞多个线程,直至另一线程修改共享变量(条件)并通知 condition_variable
;有意修改变量的线程必须:
- 获得
std::mutex
(常通过 std::lock_guard ) - 在保有锁时进行修改
- 在
std::condition_variable
上执行 notify_one 或 notify_all (不需要为通知保有锁)
即使共享变量是原子的,也必须在互斥下修改它,以正确地发布修改到等待的线程。
任何有意在 std::condition_variable
上等待的线程必须
- 在与用于保护共享变量者相同的互斥上获得 std::unique_lock<std::mutex>
- 执行下列之一:
- 检查条件,是否为已更新或提醒它的情况
- 执行 wait 、 wait_for 或 wait_until ,等待操作自动释放互斥,并悬挂线程的执行。
- condition_variable 被通知时,时限消失或虚假唤醒发生,线程被唤醒,且自动重获得互斥。之后线程应检查条件,若唤醒是虚假的,则继续等待。
wait
#include <iostream>
#include <string>
#include <condition_variable>
#include <mutex>
#include <thread>
using namespace std;
mutex m;
condition_variable cv;
bool ready = false;
bool processed = false;
std::string _data;
void worker_thread()
{
cout << " worker_thread start " << endl;
unique_lock<mutex> lk(m);//加锁
/*
* wait 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,
* 可选地循环直至满足某谓词。wait 导致当前线程阻塞直至条件
* 变量被通知,或虚假唤醒发生,可选地循环直至满足某谓词。
*
*原子地解锁 lock ,阻塞当前执行线程,并将它添加到于 *this 上等待的线程列表。
*线程将在执行 notify_all() 或 notify_one() 时被解除阻塞。解阻塞时,无关乎原因,
*lock 再次锁定且 wait 退出。若此函数通过异常退出,则亦会重获得 lock
*/
cv.wait(lk, [] { return ready; });//此时会解锁,直至 notify_all() 或 notify_one() 的通知
/*
* 等价于
* while (!ready) // 如果标志位不为 true, 则等待...
* cv.wait(lk); // 当前线程被阻塞, 当全局标志位变为 true 之后, 线程被唤醒, 继续往下执行打印线程编号id.
*/
cout << "worker thread is processing data" << endl;
_data += "after processing";
processed = true;
cout << "worker thread signals data processing completed" << endl;
lk.unlock();
cv.notify_one();//唤起main thread 继续执行
}
void print(int i)
{
cout << i << endl;
}
int main()
{
thread worker(worker_thread);
_data = "Example data";
{
cout << "main thread locked " << endl;
lock_guard<mutex> lk(m);
ready = true;
cout << "main signals data ready for processing " << endl;
}
cv.notify_one();//通知worker_thread,让其继续执行
{
/*
* mian thread 会在此处wait,等待worker_thread的通知
*/
unique_lock<mutex> lk(m);
cv.wait(lk, [] { return processed; });
}
cout << "back in main(),data= " << _data.c_str() << endl;
worker.join();
return system("pause");
}
wait函数执行的步骤:
- unlock mutex,wait调用要和mutex配合,调用wait前要先获取mutex的锁,调用wait时会先自动解锁,使得其他被阻塞在锁竞争上的线程得以继续执行。
- waiting for notify,阻塞等待唤醒;
- waked by notify,被唤醒;
- lock mutex,自动重新加锁,使得mutex状态和wait被调用时相同;
wait_for
可以指定一个时间段,在当前线程收到通知或者指定的时间 rel_time 超时之前,该线程都会处于阻塞状态;
wait_until
可以指定一个时间点,在当前线程收到通知或者指定的时间点 abs_time 超时之前,该线程都会处于阻塞状态;
notify_one
唤醒某个等待(wait)线程。如果当前没有等待线程,则该函数什么也不做,如果同时存在多个等待线程,则唤醒某个线程是不确定的(unspecified)。
#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;
bool ready = false;
void do_read_value()
{
std::cout << "Please, enter an integer (I'll be printing dots): \n";
std::cin >> value;
ready = true;
cv.notify_one();
std::cout << "end " << std::endl;
}
bool isReady()
{
return ready;
}
int main()
{
std::thread th(do_read_value);
std::mutex mtx;
std::unique_lock<std::mutex> lck(mtx);
bool loop = true;
while (cv.wait_for(lck, std::chrono::seconds(1)) == std::cv_status::timeout) {
std::cout << "timeout..." << "\n";
std::cout.flush();
}
std::cout << "You entered: " << value << '\n';
th.join();
return 0;
}
notify_all
唤醒所有的等待(wait)线程。如果当前没有等待线程,则该函数什么也不做。
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
std::condition_variable cv;
std::mutex cv_m; // 此互斥用于三个目的:
// 1) 同步到 i 的访问
// 2) 同步到 std::cerr 的访问
// 3) 为条件变量 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();
}
notify_all_at_thread_exit
condition_variable_any