繁体   English   中英

是否有用于 std::condition_variable 的 notify_one() 队列?

[英]Is there a `notify_one()` queue for `std::condition_variable`s?

考虑第一个线程 function 和全局变量:

    std::mutex mut;
    std::condition_variable officer;
    bool firstPlayerIsReady = false;
    bool secondPlayerIsReady = false;

void firstPlayer(){
    constexpr auto doIt = true;
    while(doIt)
    {
        std::unique_lock lock{mut};
        auto toContinue = ring();
        secondPlayerIsReady = true;
        firstPlayerIsReady = false;
        officer.notify_one();   //#1
        if(!toContinue) return;
        officer.wait(lock,[=](){ return firstPlayerIsReady;});
    }
}

它调用一些 ring 并且 ring() 返回一个继续条件; 然后它会在下一个循环中更新每个线程的就绪值;

考虑下一个线程:

void secondPlayer(){
    constexpr auto doIt = true;
    while(doIt)
    {
        auto period = std::chrono::seconds(5);
        std::this_thread::sleep_for(period);

        std::unique_lock lock{mut};   //#2
        officer.wait(lock,[this](){ return secondPlayerIsReady;});
        auto toContinue = ring();
        firstPlayerIsReady = true;
        secondPlayerIsReady = false;
        officer.notify_one();
        if(!toContinue) return;
    }
}

该线程等待 5 秒后被 wait() 锁定,直到第一个线程调用 notify_one(); 此外,类似于第一个线程。

先验地,带有#1 标记的行比带有#2 标记的行执行得早,因此通知在第二个线程被锁定之前发送。 问题是——是否有一个 notify_one ( ) 队列? 否则,显然不会发送通知。

没有队列。 如果一个线程调用notify_one并且没有其他线程在等待,它将不会执行任何操作。

这就是为什么在您的示例中有谓词的原因

officer.wait(lock,[this](){ return secondPlayerIsReady;});

因此,当线程调用此方法时,如果secondPlayerIsReady为true,则该线程将完全不等待,而仅跳过该行。

因此,只要正确设置该标志,就notify_one太早地调用notify_one没问题。 只需记住,修改后该标志需要由互斥体保护。

@super 完全正确,但我真的很难理解这个概念,所以我想用我自己的话来表达并添加我自己的答案。 开始:

不, std::condition_variable没有底层通知队列......

...但这根本不重要,只要您使用 boolean 谓词,消费者就不会错过在消费者点击wait() function之前发送的早期通知(由生产者发出)!

这是因为此调用将 lambda function boolean 谓词作为第二个参数:

cv.wait(lock, []() { 
    return sharedData.isNewData; 
});

...与这个while循环完全相同

while (sharedData.isNewData == false) // OR: `while (!sharedData.isNewData)`
{
    cv.wait(lock);
}

...和 while 循环的cv.wait(lock); line 只有在谓词一开始就为false时才会被调用!

详细:

没有底层通知队列,也没有计数器,也没有 boolean 标志。 相反,我们检查的boolean 谓词标志,而且,它在wait() function 开始时,睡眠之前,以及wait() function 结束时,在睡眠之后和每次线程时都被检查醒来。 wait() function在谓词开始为false时才使线程休眠,并且仅在谓词为true时通过返回退出wait() function。 看看上面的while循环版本,这将变得非常清楚。

因此,如果生产者线程将共享谓词设置为true ,然后发送my_condition_variable.notify_one()调用,如果消费者线程尚未等待,则它不会收到该通知。 但是,这并不重要! 只要消费者线程使用 2 参数wait()调用(第二个参数是 boolean 谓词),或者使用while循环谓词检查技术,那么一旦消费者线程命中wait()调用(或 while 循环),谓词将被视为true ,整个 while 块和等待睡眠将被完全跳过,消费者将提前 go 并立即运行,就像等待时收到notify_one()通知一样,当使用谓词时,最终结果与条件变量确实具有长度为 1 的基础通知队列(或标志)相同。

看:

  1. https://en.cppreference.com/w/cpp/thread/condition_variable/wait - 它指出这个模板化的 function:
     template< class Predicate > void wait( std::unique_lock<std::mutex>& lock, Predicate stop_waiting );
    是:

    相当于

    while (;stop_waiting()) { wait(lock); }
  2. @super的回答,他让我走上了正确的轨道,让我自己终于明白了这一点

也可以看看

  1. 我记录了上述信息,以及std::condition_variable的详细完整示例以及如何在我的eRCaGuy_hello_world 存储库中的生产者-消费者线程安全队列中使用它: std_mutex_vs_std_lock_guard_vs_std_unique_lock_vs_std_scoped_lock_README.md

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM