考虑以下简化示例:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mutex;
std::condition_variable cv;
bool cv_flag = false; // I'm talking about this flag here
void startThread1()
{
std::cout << "thread 1 prints first\n";
{
// Set the flag (lock to be safe)
std::unique_lock<std::mutex> lock(mutex);
cv_flag = true;
}
cv.notify_one();
}
void startThread2()
{
std::unique_lock<std::mutex> lock(mutex);
if (!cv_flag)
{
cv.wait(lock);
}
std::cout << "thread 2 prints second\n";
}
int main()
{
std::thread thread1(startThread1);
std::thread thread2(startThread2);
thread1.join();
thread2.join();
}
此处,cv_flag
用于确保线程2未锁定,wait()
如果线程1已使用notify_one()
发送通知。没有它,线程2可能会在线程1已经调用wait()
之后锁定notify_one()
,导致无限期挂起,因为线程2正在等待已经发生的事情。
我见过很多这样的代码,cv_flag
之类的内容仅用于检测可能错过的通知。
这真的是唯一的方法吗?最干净最简单的?我想如果你能做到这样的话会很棒:
std::mutex mutex;
std::condition_variable cv;
// no more need for cv_flag
void startThread1()
{
std::cout << "thread 1 prints first\n";
cv.notify_one();
}
void startThread2()
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait_unless_already_notified(lock); // Unfortunately, this function doesn't exist
std::cout << "thread 2 prints second\n";
}
是否有类似wait_unless_already_notified()
的内容?如果没有,是否有技术原因不存在?
编辑:更改信号/信号对通知/通知/通知的引用以消除歧义。
答案 0 :(得分:7)
条件变量不用于检测信号!条件变量的目的是等待一个或多个线程完成某些可被检测为未完成的线程。该信号仅表示另一个线程已经改变某些东西,等待线程应该重新评估它正在等待的条件。除了发送到条件变量的信号之外,还需要更改其他内容以等待。如果你想检测另一个线程是否刚刚发送了一些信号,你需要另一个线程来设置相应的指示。
请注意,您的代码存在问题:wait()
不一定会因发送信号而唤醒。它可以唤醒由于虚假唤醒而没有另一个线程发出信号。也就是说,您需要始终使用wait()
重新评估条件,例如:
while (!cv_flag) { cv.wait(lock); }
cv.wait(lock, [&](){ return cv_flag; });
答案 1 :(得分:4)
cv_flag
仅用于检测可能遗漏的信号。
您的信息不完整。旗帜的主要功能不是完全来检测错过的通知(与信号不同,请参阅Dietmar的评论),但主要是为了防止虚假的唤醒condition_variable
(换句话说,即使没有人调用cv.wait()
函数之一,cv.notify_*()
也可以返回if (!cv_flag)
{
cv.wait(lock);
}
。说到这,你的示例代码是错误的:
while
它应该是while (!cv_flag)
{
cv.wait(lock);
}
循环:
wait_unless_already_signaled()
是否有类似
condition_variable
的内容?如果没有,是否有技术原因不存在?
由于已经提到过可能的虚假唤醒,所以必要有一个单独的变量来反映&#34; real&#34;事件的状态,至少是一个布尔标志,这样如果while
虚假地醒来(因此if
循环而不仅仅是wait_unless_already_signaled()
),你就可以继续等待。当然,这个要求会使你提出的{{1}}无用,这就解释了它为什么不存在。