std::condition_variable wait() 和 notify_one() 同步

std::condition_variable wait() and notify_one() synchronization

前言:我在这里看到过类似的问题,但似乎没有一个能回答我的问题。

是否有可靠的方法来确保在生产者线程的第一个 notify_one() 调用之前调用消费者线程中的 wait() 方法?

即使在消费者线程中使用unique_lock,生产者线程也有可能会先运行,锁定互斥量并在消费者调用[=14之前调用notify() =],因此,我的应用程序将缺少第一个 notify() 调用。

编辑:感谢您的所有回答,它们确实帮助了我。我的问题是这个消费者循环中的第一个 wait-notify():

while (!timeToQuit) {
    gdcv.wait(gdcondlock);
    gdlock.lock();
    //spurious wakeup
    if (gdQueue.empty()) {
      gdlock.unlock();
      continue;
    }
    //some work here
    gdlock.unlock();
} 

我想我必须为第一个循环迭代编写额外的代码。

EDIT2:这个循环和第二个锁(unique_lock btw)在那里是因为有多个生产者和消费者访问队列。

EDIT3:在 boost::lockfree::queue 的帮助下等待此特定线程的正确方法,以防有人遇到类似问题:

  nfq_data* data;
  while (!timeToQuit) {
    gdcv.wait(gdlock,[&]{return !gdQueue.empty() || timeToQuit;});
    gdQueue.pop(data);
    gdlock.unlock();
  }

不,由您负责线程同步。

如果您不想错过通知调用,即使它发生在消费者开始等待之前,您必须控制在某处记录 producer 已完成,然后根本不调用 wait() 函数。

例如,您可以实现一种 事件 class,仅当事件尚未发生时才等待条件变量:

#include <mutex>
#include <condition_variable>

class Event
{
public:
    Event();
    void set_event();
    void reset_event();
    void wait_event();
private:
    std::mutex mtx;
    std::condition_variable cv;
    bool is_set;
};

Event::Event()
: is_set{false}
{}

void Event::set_event()
{
    std::lock_guard<std::mutex> lck{mtx};
    is_set = true;
    cv.notify_all();
}

void Event::reset_event()
{
    std::lock_guard<std::mutex> lck{mtx};
    is_set = false;
}

void Event::wait_event()
{
    std::unique_lock<std::mutex> lck{mtx};
    if( is_set )
        return;

    cv.wait(lck, [this]{ return is_set;} );
}

Even with unique_lock in consumer thread there is a possibility that producer thread will run first, lock the mutex and call noify() before consumer calls wait(), therefore, my app will be missing first nofity() call.

消费者要么需要等待,要么没有。如果它有什么要等待的,那就没有问题。如果没有任何等待,请不要调用 wait。真的就是这么简单。

当且仅当您想要等待时才致电 wait

条件变量的存在是为了解决如何释放锁并等待的问题,而不必冒等待已经发生的事情的风险。他们通过提供一个自动释放锁并等待的函数来解决这个问题。他们不会错过唤醒,因为他们在决定睡觉时持有锁。

Even with unique_lock in consumer thread there is a possibility that producer thread will run first, lock the mutex and call noify() before consumer calls wait(), therefore, my app will be missing first nofity() call

如果生产者已经运行,那么消费者不必等待,因此不应该调用wait。如果消费者仅在需要时等待 - 并且条件已同步 - 那么它不会错过需要注意的通知。

听起来您正试图(误)使用 condition_variable 来实现 "barrier"。

条件变量允许您等待某些 条件 变为真,通过某些谓词进行测试,例如"there is work available",你应该总是在等待之前测试谓词,这确保你不会 "miss" 事件并在你应该工作的时候等待。

使用条件变量纯粹等待,没有关联的谓词,效果不佳。这不是它们的设计用途。

如果您试图让所有线程在代码中的特定点等待并且仅在它们全部到达时才继续,那么您使用的概念略有不同,称为屏障。

C++ 并发 TS 为 C++ 标准库定义了障碍(以及稍微简单一些的概念 "latches"),请参阅草案 N4538

您可以通过定义带有计数器的 class 来自己定义屏障,该计数器在内部使用 condition_variable。它需要等待的条件是"all N threads have incremented the counter"。然后你可以让生产者和所有的消费者都在屏障处等待,它们都会阻塞,直到最后一个线程到达屏障。即使生产者到达屏障先开始等待,你保证消费者也会在屏障处停下来等待,直到所有线程到达它,然后它们都会继续。