std::condition_variable::notify_one() 不唤醒等待线程
std::condition_variable::notify_one() does not wake up a waiting thread
我有如下代码:
#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()
没有帮助。
条件变量的这种用法是否正确?
我不得不回到原来的答案,为此我向 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();
}
对不起。这个问题是完全错误的。
我认为 "notification should be fired after the main thread have begun to wait" 的说法仍然正确,但挂起的根本原因是 虚假唤醒,正如 Jive Dadson 和 Erik Alapää 指出的那样。
有一个原因我无法编译代码w/o优化选项,所以我误解了挂起点,因为调试器指向的点不是很清楚。挂起点不是行 cv.wait(lg)
。它在 // do something 3
.
里面的某个地方
我有一个标志,如果 // do something 1
已经成功,它就会被设置,而如果在 // do something 1
中抛出异常,它就会被清除。在 // do something 3
中检查标志,如果它指示失败,则主线程调用 worker.get()
重新抛出异常。由于 // do something 2
是无限循环的一种形式,如果 cv
被虚假唤醒导致标志尚未设置,则主线程将挂起。
现在可以正常使用了!谢谢大家。
我有如下代码:
#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()
没有帮助。
条件变量的这种用法是否正确?
我不得不回到原来的答案,为此我向 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();
}
对不起。这个问题是完全错误的。 我认为 "notification should be fired after the main thread have begun to wait" 的说法仍然正确,但挂起的根本原因是 虚假唤醒,正如 Jive Dadson 和 Erik Alapää 指出的那样。
有一个原因我无法编译代码w/o优化选项,所以我误解了挂起点,因为调试器指向的点不是很清楚。挂起点不是行 cv.wait(lg)
。它在 // do something 3
.
我有一个标志,如果 // do something 1
已经成功,它就会被设置,而如果在 // do something 1
中抛出异常,它就会被清除。在 // do something 3
中检查标志,如果它指示失败,则主线程调用 worker.get()
重新抛出异常。由于 // do something 2
是无限循环的一种形式,如果 cv
被虚假唤醒导致标志尚未设置,则主线程将挂起。
现在可以正常使用了!谢谢大家。