![](/img/trans.png)
[英]Calling std::condition_variable::notify_one() multiple times before std::condition_variable::wait() is called
[英]std::condition_variable wait() and notify_one() synchronization
前言:我在这里看到过类似的问题,但似乎没有一个能回答我的问题。
有没有一种可靠的方法来确保在从生产者线程第一次调用notify_one()
之前调用消费者线程中的wait()
方法?
即使在消费者线程中使用unique_lock
,也有可能生产者线程首先运行,锁定互斥锁并在消费者调用wait()
之前调用notify()
wait()
,因此,我的应用程序将丢失第一个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();
}
即使在消费者线程中使用 unique_lock ,也有可能生产者线程将首先运行,锁定互斥锁并在消费者调用 wait() 之前调用 noify(),因此,我的应用程序将丢失第一个 nofity() 调用。
要么消费者有什么需要等待的,要么没有。 如果它有什么要等待的,那就没有问题。 如果它没有什么可等待的,请不要调用wait
。 真的就是这么简单。
呼叫wait
,当且仅当,你要等待。
条件变量的存在是为了解决如何释放锁并等待而不冒等待已经发生的事情的风险的问题。 他们通过提供一个原子地释放锁并等待的函数来解决它。 他们不能错过唤醒,因为他们在决定睡觉时持有锁。
不,由您来处理线程同步。
如果你不想错过通知调用,即使它发生在消费者开始等待之前,你必须控制这种可能性,记录生产者完成的某处,然后根本不调用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;} );
}
听起来您正试图(错误地)使用condition_variable
来实现“障碍”。
条件变量允许您等待某个条件变为真,通过某个谓词进行测试,例如“有可用的工作”,并且您应该始终在等待之前测试谓词,以确保您不会“错过”事件并等待你应该在什么时候工作。
仅使用条件变量等待,而没有关联的谓词,效果不佳。 这不是它们的设计用途。
如果您试图让所有线程在代码中的特定点等待,并且仅在它们全部到达时才继续,那么您使用的概念略有不同,称为屏障。
C++ 并发 TS 为 C++ 标准库定义了障碍(以及稍微简单的“闩锁”概念),请参阅草案N4538 。
您可以通过定义一个带有计数器的类来自己定义屏障,该类在内部使用条件变量。 它需要等待的条件是“所有 N 个线程都增加了计数器”。 然后你可以让生产者和所有消费者在屏障处等待,他们都会阻塞,直到最后一个线程到达屏障。 即使生产者到达屏障并首先开始等待,您也可以保证消费者也会在屏障处停止等待,直到所有线程到达它,然后它们都会继续进行。
即使在消费者线程中使用 unique_lock 也有可能生产者线程将首先运行,锁定互斥锁并在消费者调用 wait() 之前调用 noify(),因此,我的应用程序将丢失第一个 nofity() 调用
如果生产者已经运行,那么消费者不必等待,因此不应调用wait
。 如果消费者只在需要的时候等待——并且条件是同步的——那么它不会错过它需要注意的通知。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.