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

时间:2015-11-20 10:37:01

标签: c++ multithreading c++11

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

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

即使在消费者线程中使用unique_lock,生产者线程也有可能首先运行,锁定互斥锁并在消费者调用wait()之前调用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();
  }

4 个答案:

答案 0 :(得分:5)

  

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

消费者要么等待,要么没有。如果它有等待的东西,没有问题。如果它没有任何等待的东西,请不要打电话给“等待”。真的很简单。

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

存在条件变量来解决如何释放锁并等待的问题,而不会冒你等待已经发生的事情的风险。他们通过提供原子释放锁并等待的功能来解决它​​。他们不能错过唤醒,因为他们决定睡觉时会抓住锁。

答案 1 :(得分:2)

不,由您来处理线程同步。

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

例如,您可以实现一种事件类,只有在事件尚未发生时才等待条件变量:

#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;} );
}

答案 2 :(得分:2)

听起来你正试图(误)使用condition_variable来实施“障碍”。

条件变量允许您等待某些条件变为真,通过某些谓词进行测试,例如: “有可用的工作”,你应该在等待之前测试谓词,这样可以确保你不会“错过”这个事件并等到你应该工作的时候。

使用条件变量纯粹等待,没有关联的谓词,不能正常工作。这不是他们设计使用的方式。

如果您尝试让所有线程在代码中的某个特定点等待,并且只有在它们全部到达时才继续,那么您使用的是一个稍微不同的概念,称为障碍。

C ++ Concurrency TS为C ++标准库定义了障碍(以及稍微简单的“锁存”概念),请参阅草案N4538

您可以通过使用计数器定义一个类来自己定义一个障碍,该计数器在内部使用condition_variable。它需要等待的条件是“所有N个线程都增加了计数器”。然后你可以让生产者和所有消费者在屏障处等待,他们将全部阻止,直到最后一个线程到达屏障。即使生产者到达障碍并开始先等待,也可以保证消费者也会在屏障处停下来等待,直到所有线程都到达,然后他们都会继续。

答案 3 :(得分:1)

  

即使在消费者线程中使用unique_lock,生产者线程也有可能首先运行,锁定互斥锁并在消费者调用wait()之前调用noify(),因此,我的应用程序将缺少第一个nofity()调用

如果生产者已经开始运营,那么消费者不必等待,因此不应该致电wait。如果消费者只在需要时等待 - 并且条件是同步的 - 那么它就不会错过它需要注意的通知。