【问题标题】:Does notifying a condition variable guarantee the wake-up of a thread with a successful condition/predicate if one exists?如果存在条件/谓词,通知条件变量是否保证唤醒具有成功条件/谓词的线程?
【发布时间】:2022-01-23 07:38:39
【问题描述】:

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

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

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

condition.wait(lock, [=] {
    if (!some_condition) condition.notify_one();
    return some_condition;
});

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

【问题讨论】:

  • 不保证通知哪个线程,无论哪个线程正在执行通知。可以说,如果两个线程都收到同一个 cv 的通知,但两者或其中一个都可以做有用的工作,那么两个线程都应该被通知(通知所有)。我通常为每个条件变量争取一个“条件”。推理起来更容易。
  • "主线程决定用 cond.notify_one() 随机通知其中一个。" 如果多个线程等待一个条件,则不应使用 @ 987654326@.
  • @NicolBolas 有多个等待,但我希望一次只唤醒一个,这将在其锁定期间使所有其他等待线程的谓词为假,并且然后在关键部分的末尾再次使某些线程成为真的。我会使用信号量,但只有特定类型的线程可以在任何给定时间访问临界区。
  • @MarkedasDuplicate: "这将在其锁定期间使所有其他等待线程的谓词为假,然后在结束时再次使某些线程为真临界区。”这就是所谓的“锁定互斥体”。您根本不需要更改条件即可执行此操作。
  • @MarkedasDuplicate:在我看来,这一切听起来就像您需要一个任务调度系统,而不是条件变量。您可以根据任务是在组 n 还是组 m 中来排序要执行的任务。

标签: c++ concurrency synchronization c++20 condition-variable


【解决方案1】:

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

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

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

接下来,让我们看看wait()带有谓词is logically equivalent to

while (!stop_waiting()) {
    wait(lock);
}

注意谓词,这里命名为stop_waiting当互斥锁被锁定时,它只在“真正的”wait() 之后被检查,一个不检查谓词条件的,返回。

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

  1. 使用notify_all()

  2. 无论哪个线程成功地从wait() 返回,只需要做任何需要做的事情,以便不再满足谓词条件。这样做的确切方式取决于谓词。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    相关资源
    最近更新 更多