是否有其他方法可以使用条件变量捕获可能错过的信号

Are there alternative ways to catch potentially missed signals with condition variables?

本文关键字:错过 信号 变量 其他 方法 可以使 条件 是否      更新时间:2023-10-16

考虑以下简化示例:

#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 firstn";
    {
        // 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 secondn";
}
int main()
{
    std::thread thread1(startThread1);
    std::thread thread2(startThread2);
    thread1.join();
    thread2.join();
}

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

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

这真的是唯一的方法吗?最干净、最简单的呢?我认为如果你能做这样的事情,那就太好了:

std::mutex mutex;
std::condition_variable cv;
// no more need for cv_flag
void startThread1()
{
    std::cout << "thread 1 prints firstn";
    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 secondn";
}

有没有像wait_unless_already_notified()?如果不存在,是否存在不存在的技术原因?

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

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

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

  1. while (!cv_flag) { cv.wait(lock); }
  2. cv.wait(lock, [&](){ return cv_flag; });

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

您的信息不完整。该标志的主要功能不仅仅是检测错过的通知(这与信号不同,参见 Dietmar 的评论),而主要是为了防止condition_variable的虚假唤醒(换句话说,即使没有人调用cv.notify_*()函数之一,cv.wait()也可以返回)。说到这里,你的示例代码是错误的:

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()毫无用处,这就解释了为什么它不存在。