C++ std::条件变量wait()和通知变量one()同步

C++ std::条件变量wait()和通知变量one()同步,c++,multithreading,c++11,condition-variable,C++,Multithreading,C++11,Condition Variable,前言:我在这里看到过类似的问题,但似乎没有一个能回答我的问题 是否有可靠的方法确保在生产者线程的第一次notify_one()调用之前调用使用者线程中的wait()方法 即使消费者线程中有unique\u lock,生产者线程也有可能在消费者调用wait()之前首先运行,锁定互斥锁并调用notify(),因此,我的应用程序将丢失第一个notify()调用 编辑:谢谢你的回答,他们确实帮了我。我的问题是此使用者循环中的第一个wait-notify() while (!timeToQuit) {

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

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

即使消费者线程中有
unique\u 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();
  }

不,由您负责线程同步

如果您不想错过notify调用,即使它发生在消费者开始等待之前,您必须控制这种可能性,在某个地方记录生产者已完成,然后根本不调用
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;} );
}
#包括
#包括
班级活动
{
公众:
事件();
void set_事件();
无效重置_事件();
无效等待事件();
私人:
std::互斥mtx;
std::条件变量cv;
布尔集;
};
Event::Event()
:设置为{false}
{}
void事件::set_事件()
{
锁紧保护lck{mtx};
is_set=true;
cv.通知所有人();
}
无效事件::重置_事件()
{
锁紧保护lck{mtx};
is_set=false;
}
void事件::wait_事件()
{
std::unique_lock lck{mtx};
如果(已设置)
返回;
wait(lck,[this]{return is_set;});
}
即使在消费者线程中使用唯一的_锁,生产者线程也有可能首先运行,在消费者调用wait()之前锁定互斥锁并调用noify(),因此,我的应用程序将丢失第一个nofity()调用

消费者要么有东西要等,要么没有。如果它有什么需要等待,那就没有问题了。如果它没有什么可等待的,不要调用
wait
。其实就是这么简单

如果且仅当您想等待时,请调用
wait

条件变量的存在是为了解决如何释放锁并等待而不冒等待已经发生的事情的风险的问题。他们通过提供一个原子释放锁并等待的函数来解决这个问题。他们不能错过一次醒来,因为当他们决定睡觉时,他们紧握着锁

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


如果生产者已经运行,则消费者不必等待,因此不应调用
wait
。如果消费者只在需要时才等待,并且条件是snychronized,那么它就不能错过需要通知的通知。

听起来好像你试图(错误地)使用
条件变量来实现“屏障”

条件变量允许您等待某个条件变为真,通过某个谓词进行测试,例如“有可用的工作”,并且您应该始终在等待前测试谓词,这确保您不会“错过”事件,并在应该工作时等待

如果只使用条件变量等待,而不使用关联的谓词,则效果不佳。这不是设计用来使用它们的方式

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

<> C++并发性定义了C++标准库中的障碍(以及简单的“锁存””概念,参见草案。
您可以通过定义一个带有计数器的类来定义屏障,该计数器在内部使用条件变量。它需要等待的条件是“所有N个线程都增加了计数器”。然后,您可以让生产者和所有消费者在屏障处等待,他们都将阻塞,直到最后一个线程到达屏障。即使生产者到达障碍物并首先开始等待,您也可以保证消费者也会在障碍物处停止并等待,直到所有线程到达障碍物,然后他们都会继续进行。

听起来似乎您需要的是一个信号量,而不是条件变量-请参见这里的示例:为什么要调用
gdlock.lock()
gdlock.unlock()
而不是使用
lock\u-guard
?你不喜欢简单和正确吗?为什么你甚至有两个锁?听起来你甚至不需要问这样一个问题:如果代码更地道,难道不能用
lock\u-guard lg(m)替换整个东西吗;gdcv.wait(lg,[&]{return gdQueue.empty();})
?这应该是正确的做法,即如果条件为真,立即返回,或者等待通知,直到条件为真。您可能有非常特殊的情况,但在正常情况下,不需要无锁容器。修改容器、通知和等待时应使用相同的互斥锁。