我的代码如下:
#include <mutex>
#include <condition_variable>
#include <future>
std::mutex mtx;
std::condition_variable cv;
std::future<void> worker;
void worker_thread() {
{
std::lock_guard<std::mutex> lg{ mtx };
// do something 1
}
cv.notify_one();
// do something 2
}
int main() {
{
std::unique_lock<std::mutex> lg{ mtx };
worker = std::async(std::launch::async, worker_thread);
cv.wait(lg);
}
// do something 3
}
主线程没有进入// do something 3
,我无法理解为什么。我认为在主线程传递cv.notify_one()
之后应该从工作线程到达cv.wait(lg)
行,所以没有理由挂起。
工作线程负责一些流数据处理,而主线程主要负责GUI事件处理。
// do something 1
是关于应该在工作线程内完成的一些初始化。主线程应该等待工作线程完成它。
// do something 2
是工作线程的主要工作。
// do something 3
是主线程的主要工作。
将cv.notify_one()
更改为cv.notify_all()
没有帮助。
条件变量的这种用法是否正确?
答案 0 :(得分:2)
我必须回溯原来的答案,我为Junekey道歉。我误读了代码,并断定存在竞争条件。我无法重现这个问题。我们需要一个实际上在cv.wait上永久阻塞的例子,以便找出它为什么会这样做。然而,代码是不正确的,如果没有其他原因,它可能会得到虚假通知并在worker_thread调用cv.notify之前通过cv.wait。这很少发生,但它确实发生了。
此代码或多或少是规范的:
#include <mutex>
#include <condition_variable>
#include <thread>
std::mutex mtx;
std::condition_variable cv;
bool worker_done = false; // <<< puts the "condition" in condition_variable
void worker_thread() {
// do something 1
{
std::lock_guard<std::mutex> lg{ mtx };
worker_done = true; // ... and whatever
}
cv.notify_one();
// do something 2
}
int main() {
std::thread workman(worker_thread);
{
std::unique_lock<std::mutex> lg{ mtx };
while (!worker_done) {
cv.wait(lg);
}
}
// do something 3
workman.join();
}
答案 1 :(得分:0)
对不起这个问题完全错了。 我认为“主要线程已经开始等待后通知应该被解雇”仍然是正确的,但是悬挂的根本原因是虚假的觉醒,正如Jive Dadson和ErikAlapää指出的那样。 / p>
有一个原因我无法编译没有优化选项的代码,所以我误解了挂点,因为调试器指向的点不是很清楚。挂起点不是行cv.wait(lg)
。它位于// do something 3
内。
如果// do something 1
成功,我会设置一个标志,如果在// do something 1
内抛出异常,则会清除该标志。在// do something 3
中,检查标志,如果它指示失败,则主线程调用worker.get()
以重新抛出异常。由于// do something 2
是一种无限循环形式,如果cv
虚假唤醒,主线程就会挂起,因此标志尚未设置。
现在它运作正常!谢谢大家。