我最近一直在学习
std::condition_variable
,只是有一些问题我无法理解。
Cppreference 和许多其他教程都给出了这样的示例:
std::mutex m;
std::condition_variable cv;
void worker_thread()
{
// wait until main() sends data
std::unique_lock 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();
}
condition_variable
总是与mutex
配对?我不明白为什么这里需要互斥锁。[]{ return ready; }
在这里做什么?std::condition_variable
作为执行一些低级消息传递的工具而存在。 C++ std 库没有提供“信号量”或“门”或其他线程原语,而是提供了一个与硬件线程工作方式相匹配的低级原语,您可以使用它来实现其他线程原语。
std::condition_variable
提供了发生通知的钩子,而std::mutex
提供了保护数据的钩子。 事实上,在实际硬件和操作系统提供通知原语的现实世界中,硬件提供的通知原语并不是 100% 可靠,因此您需要一些数据来备份您的通知系统。
具体而言,可能会出现虚假通知 - 可能会出现与向您的
std::condition_variable
(或其底层硬件/操作系统原语)发送通知的任何人不对应的通知。
因此,当您收到通知时,必须检查一些数据(以线程安全的方式)以确定该通知是否确实对应于消息。
结果是使用
std::condition_variable
的标准方法是拥有 3 个紧密相关的部分:
std::mutex
保护一些包含可能消息的数据。std::condition_variable
。一个非常简单的消息系统可能是一扇可以打开但永远不会关闭的门。 在这里,您的数据是一个
bool
,表示“门是否打开”。 当您打开门时,您可以修改该布尔值(以线程安全的方式)并通知等待门打开的任何人。 等待门打开的代码等待条件变量;当它醒来时,它会检查门是否打开,并且仅在门确实打开时才接受通知。 如果门关闭,它会认为唤醒是虚假的,并返回睡眠状态。
实际代码中:
struct Gate {
void open() {
auto l = lock();
is_open = true;
cv.notify_all();
}
void wait() const {
auto l = lock();
cv.wait(l, [&]{return is_open;});
}
private:
mutable std::mutex m;
bool is_open = false;
mutable std::condition_variable cv;
auto lock() const { return std::unique_lock{m}; }
}
在
open
中,我们锁定互斥体(因为我们正在编辑共享状态 - 布尔值),我们编辑 bool is_open
来表示它已打开,然后我们通知每个正在等待它打开的人它确实已打开.
在
wait
方面,我们需要一个互斥体来调用cv.wait
。 然后,我们等待,直到大门打开——wait
的 lambda 版本为我们构建了一个循环,检查虚假唤醒并在发生时返回睡眠状态。