通知条件变量是否保证唤醒成功 condition/predicate 的线程(如果存在)?
Does notifying a condition variable guarantee the wake-up of a thread with a successful condition/predicate if one exists?
我在cppreference上找到的信息在这方面含糊不清,所以我在这里问一下。假设我有两个线程在等待一个条件,其中一个具有真谓词,另一个具有假谓词(例如 condition.wait(lock, [=]{ return some_condition; }
)。主线程决定用 cond.notify_one()
.
随机通知其中之一
假设选择的等待线程是谓词为假的线程。线程是要隐式通知下一个(如果还有剩余),还是注定要等到虚假唤醒?
如果只有一个线程被唤醒,无论它的条件是成功还是失败,第一个线程尝试唤醒下一个线程以确保通知成功的好方法是什么?天真的修复:
condition.wait(lock, [=] {
if (!some_condition) condition.notify_one();
return some_condition;
});
除了悲观之外,“通知波”可能会重复通知相同的线程,这是无效的 + 在没有线程具有成功谓词的情况下永不停止。一个notify_all()
不行,因为我们可能会不小心结束唤醒多个满足条件的线程,同时我们最多只希望一个通过。
A notify_all() won't work, because we may accidentally end waking up
multiple threads that satisfy the condition, meanwhile we only want a
single one to go through at most.
这并不完全准确。无论如何,一次只有一个线程可以锁定给定的互斥量。如果等待条件变量的所有执行线程在开始等待条件变量之前都锁定了同一个互斥量(它们应该如此),那么只有一个执行线程会成功地重新锁定互斥量并“唤醒”, return 来自 wait()
。当它解锁互斥量时,下一个计划执行线程将能够重新锁定它并从其 wait()
return。等等。 notify_all()
不会导致所有执行线程全速前进。实际上一次只有一个线程被唤醒,因为它们都必须重新锁定同一个互斥锁。这是单线程的。
所有执行线程都安排被notify_all
唤醒,它们都会被唤醒。然而,实际上,只有一个执行线程会首先被唤醒,并锁定互斥锁。当它解锁互斥量时,下一个执行线程,被安排由 notify_all()
唤醒,将能够重新锁定它,依此类推。
接下来,让我们看一下wait()
和谓词is logically equivalent to:
while (!stop_waiting()) {
wait(lock);
}
请注意,这里命名为 stop_waiting
的谓词被检查 ,而互斥体被锁定 ,并且仅被检查在“真正的”wait()
之后,不检查谓词条件的 returns.
因此,解决您的问题比您想象的要简单:
使用notify_all()
。
无论哪个线程从 wait()
成功 returning 都只需要做任何需要做的事情,以便不再满足谓词条件。这样做的确切方式取决于谓词。
因此,最终,其中一个执行线程将被唤醒,并将“关闭”谓词条件。这个执行线程解锁mutex后,其他的都会被唤醒,但是predicate条件已经不满足了。结束。
我在cppreference上找到的信息在这方面含糊不清,所以我在这里问一下。假设我有两个线程在等待一个条件,其中一个具有真谓词,另一个具有假谓词(例如 condition.wait(lock, [=]{ return some_condition; }
)。主线程决定用 cond.notify_one()
.
假设选择的等待线程是谓词为假的线程。线程是要隐式通知下一个(如果还有剩余),还是注定要等到虚假唤醒?
如果只有一个线程被唤醒,无论它的条件是成功还是失败,第一个线程尝试唤醒下一个线程以确保通知成功的好方法是什么?天真的修复:
condition.wait(lock, [=] {
if (!some_condition) condition.notify_one();
return some_condition;
});
除了悲观之外,“通知波”可能会重复通知相同的线程,这是无效的 + 在没有线程具有成功谓词的情况下永不停止。一个notify_all()
不行,因为我们可能会不小心结束唤醒多个满足条件的线程,同时我们最多只希望一个通过。
A notify_all() won't work, because we may accidentally end waking up multiple threads that satisfy the condition, meanwhile we only want a single one to go through at most.
这并不完全准确。无论如何,一次只有一个线程可以锁定给定的互斥量。如果等待条件变量的所有执行线程在开始等待条件变量之前都锁定了同一个互斥量(它们应该如此),那么只有一个执行线程会成功地重新锁定互斥量并“唤醒”, return 来自 wait()
。当它解锁互斥量时,下一个计划执行线程将能够重新锁定它并从其 wait()
return。等等。 notify_all()
不会导致所有执行线程全速前进。实际上一次只有一个线程被唤醒,因为它们都必须重新锁定同一个互斥锁。这是单线程的。
所有执行线程都安排被notify_all
唤醒,它们都会被唤醒。然而,实际上,只有一个执行线程会首先被唤醒,并锁定互斥锁。当它解锁互斥量时,下一个执行线程,被安排由 notify_all()
唤醒,将能够重新锁定它,依此类推。
接下来,让我们看一下wait()
和谓词is logically equivalent to:
while (!stop_waiting()) {
wait(lock);
}
请注意,这里命名为 stop_waiting
的谓词被检查 ,而互斥体被锁定 ,并且仅被检查在“真正的”wait()
之后,不检查谓词条件的 returns.
因此,解决您的问题比您想象的要简单:
使用
notify_all()
。无论哪个线程从
wait()
成功 returning 都只需要做任何需要做的事情,以便不再满足谓词条件。这样做的确切方式取决于谓词。
因此,最终,其中一个执行线程将被唤醒,并将“关闭”谓词条件。这个执行线程解锁mutex后,其他的都会被唤醒,但是predicate条件已经不满足了。结束。