一个线程要等待另一个线程完成任务,确定完成任务的方法有几种。第一种是持续检查mutex,这种方法显然很浪费资源;第二种是每隔一段时间进行一次检查;第三种方案是使用条件变量(condition variable)
condition variable
标准库对条件变量提供了两种实现:std::condition_variable和std::condition_variable_any,前者仅限和std::mutex工作,而后者可以与任何满足最低标准的mutex工作
#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 f()
{
std::unique_lock<std::mutex> l(m); // 传给wait的只能是std::unique_lock
cv.wait(l, [] { return ready; }); // 第二个参数为false时解锁mutex阻塞线程
// 当收到其他线程notify_one时wait会被唤醒,这时会自动调用l.lock上锁,使unique_lock l恢复到wait之前的上锁状态持有该锁
data += " after processing";
processed = true;
l.unlock();
cv.notify_one();
}
int main()
{
std::thread t(f);
data = "data";
{
std::lock_guard<std::mutex> l(m);
data += " ready";
ready = true;
cv.notify_one(); // 唤醒cv.wait,重新检查ready == true
}
{
std::unique_lock<std::mutex> l(m);
cv.wait(l, [] { return processed; });
}
std::cout << data; // data ready after processing
t.join();
}
1.因为有假醒的可能,所以,每当条件变量被唤醒,都需要重新检查条件
2.condition_variable的构造函数失败,会抛出system_error异常,并夹带错误码resource_unavailable_try_again
3.condition_variable的复制和赋值都是不允许的
4.通知都会同步化,所以并发调用notify_one()和notify_all(),没有任何问题
5.所有等待condition_variable的线程必须使用相同的mutex
6.wait()成员函数调用之前,必须用unique_lock锁住互斥量,否则结果是未定义的
7.wait_for()和wait_until()有一个不接受判断式的版本,他们的返回值是以下枚举:
std::cv_status::timeout 如果发生超时
std::cv_status::no_timeout 如果发生通知
wait_for()和wait_until()有一个接受判断式的版本(判断式作为第三实参),他们返回判断式的执行结果
8.notify_all_at_thread_exit(cv,l)在调用线程退场的时候,调用notify_all(),为了在通知"等待线程"之前完成清理工作,这个清理工作绝不该造成阻塞
参考
1.https://downdemo.gitbook.io/cpp-concurrency-in-action-2ed
2.https://www.jianshu.com/p/2e82636885cc