我在cppreference上找到的信息在这方面很模糊,所以我在这里询问。假设我有两个线程正在等待一个条件,其中一个具有 true 谓词,另一个具有 false 谓词(例如
condition.wait(lock, [=]{ return some_condition; }
)。主线程决定随机通知其中一个 cond.notify_one()
.
假设选择的等待线程是谓词为假的线程。该线程是否会隐式通知下一个线程(如果还有剩余),还是注定要等待直到虚假唤醒?
如果无论条件成功还是失败,都只会唤醒一个线程,那么第一个线程尝试唤醒下一个线程以保证通知成功的好方法是什么?一个天真的修复:
condition.wait(lock, [=] {
if (!some_condition) condition.notify_one();
return some_condition;
});
除了悲观之外,“通知波”可能会重复通知相同的线程,这是无效的+在没有线程具有成功谓词的情况下永远不会停止。 A
notify_all()
不起作用,因为我们可能会意外结束唤醒满足条件的多个线程,同时我们最多只希望一个线程通过。
notify_all() 不起作用,因为我们可能会意外结束唤醒 满足条件的多个线程,同时我们只想要一个 最多只能经历一个。
这并不完全准确。无论如何,一次只有一个线程可以锁定给定的互斥体。如果所有正在等待条件变量的执行线程在开始等待条件变量之前锁定了同一个互斥锁(它们应该这样做),那么只有其中一个执行线程将成功地重新锁定互斥锁并“唤醒”,然后从
wait()
返回。当它解锁互斥锁时,下一个计划执行线程将能够重新锁定它并从其 wait()
返回。等等。 notify_all()
不会导致所有执行线程全速前进。实际上,一次只有一个线程被唤醒,因为它们都必须重新锁定同一个互斥锁。这使它们成为单线程。
所有执行线程都被安排被
notify_all
唤醒,并且它们都会被唤醒。然而,实际上,只有一个执行线程最终会首先被唤醒,并锁定互斥体。当它解锁互斥体时,计划由 notify_all()
唤醒的下一个执行线程将能够重新锁定它,依此类推。
接下来,让我们看看带有谓词
的
wait()
在逻辑上等价于 :
while (!stop_waiting()) {
wait(lock);
}
请注意,谓词(此处名为
stop_waiting
)会在互斥体锁定时被检查 ,并且仅在“真实”wait()
(不检查谓词条件的那个)返回后才进行检查。
因此,解决你的问题比你想象的要简单:
使用
notify_all()
。
无论哪个线程成功从
wait()
返回,只需执行所需的操作即可不再满足谓词条件。这样做的确切方式取决于谓词。
因此,最终,其中一个执行线程将被唤醒,并且它将“关闭”谓词条件。在该执行线程解锁互斥锁后,所有其他线程都会唤醒,但不再满足谓词条件。结局。