C++11 std::notify_all 和虚假唤醒

c++11 std::notify_all and spurious wakeup

提问人:f1msch 提问时间:6/13/2022 最后编辑:Jean-Baptiste Yunèsf1msch 更新时间:6/16/2022 访问量:303

问:

使用 C++11。 由于会导致虚假唤醒,那么为什么会保留但不是一直存在呢? 顺便说一句,可能会导致虚假唤醒吗?std::notify_allstd::notify_allstd::notify_onestd::notify_one


详细阐述我的疑虑:

当我调用 和 时,我的目的通常是实现线程同步。也就是说,更多的线程被阻塞,要等到另一个线程通知其中一个线程才能解除阻塞。std::condition_variable.wait/wait_for/wait_untilstd::notify_XXX

那我就打电话去实现这个,但为什么还有另一个,它的目的是什么,或者适合什么情况? 而在我的情况下,当我调用时,它会唤醒所有等待的线程,然后只有一个线程实际上解除了阻塞,其他线程仍然阻塞,这叫虚假唤醒吗? 如果也会调用虚假唤醒?notify_onenotify_allnotify_allnotify_allnotify_one

C++ C++11 锁定 条件变量 spurious-wakeup

评论

5赞 user17732522 6/13/2022
"由于 std::notify_all 会导致虚假唤醒“:目的是唤醒所有线程。这不是虚假的,而是有意为之的。所以我不确定你在这里担心什么。请澄清您的问题。notify_all
0赞 f1msch 6/13/2022
@user17732522 enn 我在原来的问题下添加了我的疑惑,希望这能详细说明我的疑惑,并希望能得到答案~~
0赞 Pete Becker 6/13/2022
@user17732522 -- 虚假唤醒不是由通知触发的唤醒(或在定时等待的情况下由超时触发的唤醒)。这种担忧是有道理的:所有函数都可能虚假唤醒。wait

答:

6赞 Ted Lyngmo 6/13/2022 #1

thread.condition/8.3 开始:void std::condition_variable::wait(std::unique_lock<std::mutex>& lock);

当通过调用 或调用 或虚假调用发出信号时,该函数将取消阻止。notify_­one()notify_­all()

所以打电话或不是先决条件。它可以在不调用任何内容的情况下取消阻止。notify_­one()notify_­all()


上面的引文来自“C++20 出版后初稿”,但自从它第一次为 C++11 编写以来一直保持不变。


为什么还有另一个,它的目的是什么,或者适合什么情况?notify_allnotify_all

当您希望所有等待的线程取消阻止时。一个实际情况是什么时候需要关闭。如果你的线程被阻塞了,它们将永远不会完成,并且它们将挂起。waitjoin()

带有谓词的示例,表示它应该等到 is 或 is :abortedtruequeue.empty()false

bool pop_from_queue(T& item) {
    std::unique_lock<std::mutex> lock(mtx);
    while(queue.empty() && not aborted) cv.wait(lock);
    if(aborted) return false;        // time to shutdown
    // else pick an item from the queue
    item = std::move(queue.front());
    queue.pop();
    return true;
}

当需要关闭时,另一个线程通常会在这里执行以下操作:

aborted = true;   // std::atomic<bool>
cv.notify_all();

当我调用nitify_all时,它会唤醒所有等待的线程,然后只有一个线程实际上解封,其他线程仍然阻塞,这叫虚假唤醒吗?

不。虚假唤醒是随时可能发生的唤醒。如果调用 ,等待的线程将全部按顺序唤醒 - 而不是虚假唤醒。notify_all

如果notify_one也会叫虚假唤醒?

它可能会导致虚假唤醒,但这将是一个实现细节。最好的办法是接受线程可能随时唤醒的事实,并在它们唤醒时检查谓词。

我可以精确地控制在等待线程中取消阻止的位置(谁在没有谓词的情况下调用)?condition_variable.wait

在不检查谓词的情况下,线程唯一确定的是它唤醒了。现在,如果它有正确的理由,它就不会了。

评论

0赞 f1msch 6/13/2022
但如果是这样,我可以精确地控制在等待线程中取消阻止的位置(谁在没有谓词的情况下调用了 condition_variable.wait)?
1赞 Ted Lyngmo 6/14/2022
@f1msch我试图通过更新 qestion 来回答这个问题。如果我错过了什么,请告诉我。