如果存在成功的条件/谓词,则通知条件变量是否可以保证唤醒线程?

Mar*_*ate 2 c++ concurrency synchronization condition-variable c++20

我在cppreference上找到的信息在这方面很模糊,所以我在这里询问。假设我有两个线程正在等待一个条件,其中一个具有 true 谓词,另一个具有 false 谓词(例如condition.wait(lock, [=]{ return some_condition; })。主线程决定用 随机通知其中一个cond.notify_one()

假设所选择的等待线程是谓词为假的线程。该线程是否会隐式通知下一个线程(如果还有剩余),或者注定要等待直到虚假唤醒?

如果只有一个线程被唤醒,无论其条件成功还是失败,第一个线程尝试唤醒下一个线程以获得保证成功的通知的好方法是什么?一个天真的修复:

condition.wait(lock, [=] {
    if (!some_condition) condition.notify_one();
    return some_condition;
});
Run Code Online (Sandbox Code Playgroud)

除了悲观之外,“通知波”可能会重复通知相同的线程,这是无效的+在没有线程具有成功谓词的情况下永远不会停止。Anotify_all()不起作用,因为我们可能会意外地结束唤醒满足条件的多个线程,同时我们最多只希望一个线程通过。

Sam*_*hik 8

notify_all() 不起作用,因为我们可能会意外地唤醒满足条件的多个线程,同时我们最多只希望有一个线程通过。

这并不完全准确。无论如何,一次只有一个线程可以锁定给定的互斥体。如果所有正在等待条件变量的执行线程在开始等待条件变量之前锁定了同一个互斥锁(它们应该这样做),那么只有其中一个执行线程将成功地重新锁定互斥锁并“唤醒”,并从 回来wait()。当它解锁互斥体时,下一个计划执行线程将能够重新锁定它并从其返回wait()。等等。notify_all()不会导致所有执行线程全速前进。实际上,一次只有一个线程被唤醒,因为它们都必须重新锁定同一个互斥锁。这使它们成为单线程。

所有执行线程都被安排由 唤醒notify_all并且它们都会被唤醒。然而,实际上,只有一个执行线程最终会首先被唤醒,并锁定互斥锁。当它解锁互斥体时,计划由 唤醒的下一个执行线程notify_all()将能够重新锁定它,依此类推。

接下来,让我们看看wait()谓词在逻辑上等价于什么:

while (!stop_waiting()) {
    wait(lock);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这里命名的谓词stop_waiting在互斥体被锁定时检查的,并且仅在“real”(wait()不检查谓词条件的那个)返回后才进行检查。

因此,解决您的问题比您想象的要简单:

  1. 使用notify_all()

  2. 无论哪个线程成功返回,都wait()只需执行所需的操作,以便不再满足谓词条件。这样做的确切方式取决于谓词。

因此,最终,其中一个执行线程将被唤醒,并且它将“关闭”谓词条件。在该执行线程解锁互斥锁后,所有其他线程都会唤醒,但不再满足谓词条件。结束。