条件变量
概述
条件变量是一个同步原语,允许多个线程相互通信。它允许一定数量的线程等待(可能超时)来自另一个线程的通知,它们可以继续。条件变量始终与互斥锁相关联。
std::condition_variable 是一种与 std::mutex 一起使用的同步原语,用于阻止一个或多个线程,直到另一个线程修改共享变量(条件),并通知 std::condition_variable。
打算修改共享变量的线程必须:
- 获取 std::mutex(通常通过 std::lock_guard)。
- 在拥有锁的情况下修改共享变量。
- 对 std::condition_variable 调用notify_one或notify_all(可以在释放锁后完成)。
即使共享变量是原子的,为了正确地将修改发布到等待线程,也必须在拥有mutex时,对其进行修改。
任何想要等待 std::condition_variable 的线程必须:
- 获取用于保护共享变量的互斥锁上的 std::unique_lockstd::mutex。
- 执行以下操作之一:
- 检查情况,以防已更新,并通知。
- 对 std::condition_variable 调用 wait、wait_for 或 wait_until,以原子方式释放mutex,并挂起线程执行,直到通知条件变量、超时到期,或发生虚假唤醒,然后在返回之前,以原子方式获取mutex。
- 检查条件,如果不满足,则继续等待。
或者:
- 使用 wait、wait_for 和 wait_until 的谓词重载,它执行相同的三个步骤。
std::condition_variable 仅适用于 std::unique_lockstd::mutex,它允许在某些平台上实现最大效率。 std::condition_variable_any 提供可与任何 BasicLockable 对象一起使用的条件变量,例如 std::shared_lock。
条件变量允许同时调用 wait、wait_for、wait_until、notify_one 和 notify_all 成员函数。
std::condition_variable类是一个 StandardLayoutType。它不是 CopyConstructible, MoveConstructible, CopyAssignable, 或MoveAssignable。
构造器
condition_variable();
condition_variable( const condition_variable& ) = delete;
APIs
等待APIs
API | 说明 |
---|---|
wait | 阻塞当前线程,直到条件变量被唤醒 |
wait_for | 阻塞当前线程,直到条件变量被唤醒,或指定的超时发生 |
wait_until | 阻塞当前线程,直到条件变量被唤醒,或到达指定时间点 |
唤醒APIs
API | 说明 |
---|---|
notify_one | 通知一个等待线程 |
notify_all | 通知所有等待线程 |
编程实列
下列代码说明如何在程序中使用std::condition_variable。
#include <iostream>
#include <condition_variable>
#include <string>
#include <mutex>
#include <thread>
using namespace std;
mutex m;
condition_variable cv;
string m_data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// wait until main() set variable "ready"
unique_lock lk(m);
cv.wait(lk, []{ return ready; });
// after the wait, we own the lock
cout << "Worker thread is processing data\n";
m_data += " has been processed";
// set variable "processed" to tell main worker thread done
processed = true;
cout << "Worker thread 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()
{
thread worker(worker_thread);
m_data = "Example data";
// set variable "ready" to activate the worker thread
{
lock_guard lk(m);
ready = true;
cout << "data ready for processing\n";
}
cv.notify_one();
// wait for the worker thread done
{
unique_lock lk(m);
cv.wait(lk, []{ return processed; });
}
cout << "data = " << m_data << '\n';
worker.join();
}
data ready for processing
Worker thread is processing data
Worker thread data processing completed
data = Example data has been processed