【问题标题】:When can std::condition_variable be used without a predicate?什么时候可以在没有谓词的情况下使用 std::condition_variable?
【发布时间】:2016-01-26 11:30:34
【问题描述】:

如果std::condition_variable 可以由于虚假唤醒而发出信号(并且我们不能确定我们需要的条件是否真的得到满足),为什么 C++ 标准库在没有谓词的情况下提供 wait() 方法的重载?可以使用这种行为的场景有哪些?

【问题讨论】:

  • 我只能猜测,因为传统上 posix 和 windows API 提供的条件变量没有谓词,所以委员会希望保留它们的原始用途/API,但具有标准的、可移植的形式。除此之外,我认为没有理由使用没有谓词的 cv
  • 从历史上看,系统调用是被接受的,例如。那些用于线程间通信的,每次调用时都能正常工作。
  • @MartinJames 实现无法防止虚假唤醒。这不是系统调用是否正常工作的问题,而是决定是否阻塞的线程和未阻塞但尚未运行的线程之间的固有竞争。 (例如,两个消息进入队列。我们唤醒两个线程。如果第一个线程处理这两个消息,第二个线程将看到虚假唤醒。)

标签: c++ multithreading c++11 condition-variable spurious-wakeup


【解决方案1】:

假设一个复杂的条件A || B。当条件的任何部分为真时,应执行适当的操作,actionAactionB

使用predicate版本,代码如下:

cond.wait(lock, []{return (A || B);});
if(A) {
    actionA();
}
else {
    actionB();
}

但如果使用非谓词等待,代码可能更快

while(true)
{
    if(A) {
         actionA();
         break;
    }
    else if(B) {
         actionB();
         break;
    }
    cond.wait(lock);
}

注意,与第一个变体不同,现在每个条件部分都被评估一次。

还有更复杂的情况,即条件不能写在单个表达式中。

【讨论】:

  • 如果您想象复杂的代码,其中谓词测试中的不同内容导致不同的控制流,您可能会得到一个过于复杂的谓词或丑陋的结构,只是为了从谓词中获取信息并恢复控制流中的正确点。
  • @DavidSchwartz:这取决于。具有不同反应的两部分条件的最简单示例是读取可能已结束的队列(EOF)。至于over-complexugly structure - 多线程本身就是一件复杂的事情。有时为了简化多线程方面而牺牲程序的可读性是可取的。
【解决方案2】:

为什么 C++ 标准库在没有谓词的情况下提供了 wait() 方法的重载

The predicate version of wait 等价于:

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

如果您需要在等待之前和/或之后执行更复杂的代码,您可能希望使用不带谓词的wait

【讨论】:

    【解决方案3】:

    我猜在某些情况下,虚假唤醒并不是世界末日。

    例如,考虑一个生产者-消费者批处理系统,其中一些线程应该在队列中有至少 100 条消息时唤醒并处理它们。

    考虑到虚假唤醒,它有时可能会收到少于 100 条消息。这种差异可能无法保证条件函数的开销和额外复杂性。

    【讨论】:

    • 这是一个示例,您要么必须将实际代码用于获取谓词中的消息以避免额外开销,要么使用不进行任何测试的虚拟谓词。这些解决方案中的任何一个都是丑陋的。
    猜你喜欢
    • 2019-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    • 2017-02-21
    • 2014-08-28
    • 1970-01-01
    相关资源
    最近更新 更多