1

考虑以下简化示例:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mutex;
std::condition_variable cv;
bool cv_flag = false; // I'm talking about this flag here

void startThread1()
{
    std::cout << "thread 1 prints first\n";
    {
        // Set the flag (lock to be safe)
        std::unique_lock<std::mutex> lock(mutex);
        cv_flag = true;
    }
    cv.notify_one();
}

void startThread2()
{
    std::unique_lock<std::mutex> lock(mutex);
    if (!cv_flag)
    {
        cv.wait(lock);
    }

    std::cout << "thread 2 prints second\n";
}

int main()
{
    std::thread thread1(startThread1);
    std::thread thread2(startThread2);

    thread1.join();
    thread2.join();
}

在这里,cv_flag用于确保线程 2 没有锁定以及wait()线程 1 是否已经发送了通知notify_one()。没有它,线程 2 可能会在线程 1 已经调用wait() 之后锁定notify_one(),从而导致无限期挂起,因为线程 2 正在等待已经发生的事情。

我见过很多这样的代码,其中类似的东西cv_flag仅用于检测可能错过的通知。

这真的是唯一的方法吗?最干净和最简单的?我认为如果您可以执行以下操作会很棒:

std::mutex mutex;
std::condition_variable cv;
// no more need for cv_flag

void startThread1()
{
    std::cout << "thread 1 prints first\n";
    cv.notify_one();
}

void startThread2()
{
    std::unique_lock<std::mutex> lock(mutex);
    cv.wait_unless_already_notified(lock); // Unfortunately, this function doesn't exist

    std::cout << "thread 2 prints second\n";
}

有什么类似的wait_unless_already_notified()吗?如果没有,是否有技术原因导致它不存在?

编辑:将信号/信号引用更改为通知/通知/通知以消除歧义。

4

2 回答 2

7

条件变量不用于检测信号!条件变量的目的是等待一个或多个线程完成可以被检测为尚未完成的事情。该信号仅表明另一个线程已更改某些内容,等待线程应重新评估它正在等待的条件。除了发送到条件变量的信号之外,还需要更改其他内容以等待。如果要检测另一个线程是否刚刚发送了一些信号,则需要另一个线程设置相应的指示。

请注意,您的代码是有问题的:wait()不一定会因为发送信号而唤醒。它可以由于虚假唤醒而唤醒,而没有另一个线程发出信号。也就是说,您需要始终使用wait()重新评估条件,例如:

  1. while (!cv_flag) { cv.wait(lock); }
  2. cv.wait(lock, [&](){ return cv_flag; });
于 2013-09-09T06:07:41.340 回答
4

cv_flag仅用于检测可能丢失的信号。

您的信息不完整。该标志的主要功能不仅condition_variable检测错过的通知(这与信号不同,请参阅Dietmar的评论),但主要cv.wait()是为了防止虚假唤醒cv.notify_*()函数)。说到这,您的示例代码是错误的:

if (!cv_flag)
{
    cv.wait(lock);
}

它应该是一个while循环:

while (!cv_flag)
{
    cv.wait(lock);
}

有什么类似的wait_unless_already_signaled()吗?如果没有,是否有技术原因导致它不存在?

由于已经提到了可能的虚假唤醒,因此必须有一个单独的变量来反映“真实”事件的状态,至少是一个布尔标志,以便在condition_variable虚假唤醒时您可以继续等待(因此while循环而不是仅仅if)。当然,这个要求使您的提议wait_unless_already_signaled()毫无用处,这就解释了为什么它不存在。

于 2013-09-09T05:54:55.740 回答