std::condition_variable wait() and notify_one() synchronizat
std::condition_variable wait() and notify_one() synchronization
前言:我在这里看到过类似的问题,但似乎没有一个能回答我的问题。
有没有可靠的方法可以确保在生产者线程的第一个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)在那里,因为有多个生产者和消费者访问队列。
编辑3:在boost::lockfree::queue
的帮助下,在这个特定线程上等待的正确方法,以防有人遇到类似的问题:
nfq_data* data;
while (!timeToQuit) {
gdcv.wait(gdlock,[&]{return !gdQueue.empty() || timeToQuit;});
gdQueue.pop(data);
gdlock.unlock();
}
即使在使用者线程中有unique_lock,生产者线程也有可能首先运行,锁定互斥对象并在使用者调用wait()之前调用noify(),因此,我的应用程序将丢失第一个noity()调用。
要么消费者有东西要等,要么没有。如果它有什么需要等待的,那就没有问题。如果没有什么可等待的,请不要调用wait
。真的就这么简单。
如果并且仅当您想等待时,请致电wait
。
条件变量的存在是为了解决如何释放锁并等待的问题,而不必冒等待已经发生的事情的风险。他们通过提供一个原子释放锁并等待的函数来解决这个问题。他们不会错过唤醒,因为当他们决定睡觉时,他们会锁住锁。
听起来您正试图(错误地)使用condition_variable
来实现"屏障"。
条件变量允许您等待某个条件变为true,通过某个谓词进行测试,例如"有工作可用",并且您应该在等待之前始终测试谓词,这可以确保您不会"错过"事件并在应该工作的时候等待。
如果只使用条件变量进行等待,而不使用相关的谓词,则效果不佳。这不是它们的设计用途。
如果您试图让所有线程在代码中的某个特定点等待,并且只有当它们都到达时才继续,那么您使用的是一个稍微不同的概念,称为屏障。
C++并发TS为C++标准库定义了屏障(以及稍微简单一点的"锁存器"概念),请参见草案N4538。
您可以通过定义一个带有计数器的类来自己定义屏障,计数器在内部使用condition_variable。它需要等待的条件是"所有N个线程都增加了计数器"。然后你可以让生产者和所有消费者在屏障处等待,他们都会阻塞,直到最后一个线程到达屏障。即使生产者到达障碍并首先开始等待,也可以保证消费者也会在障碍处停止并等待,直到所有线程都到达它,然后他们都会继续。
不,由您负责线程同步。
如果您不想错过通知调用,即使它发生在消费者开始等待之前,您必须控制这种可能性,在生产者完成的地方录制,然后根本不调用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;} );
}
即使在消费者线程中有unique_lock,生产者线程也有可能首先运行,锁定互斥对象并在消费者调用wait()之前调用noify(),因此,我的应用程序将丢失第一个调用的noity()
如果生产者已经运行,那么消费者不必等待,因此不应该调用wait
。如果消费者只在需要的时候等待,并且条件被snychronized,那么它就不会错过需要通知的通知。
- condition_variable.notify是同步点吗
- 对于属性上的 NOTIFY 信号,如果我给它一个参数有什么区别?
- QApplication::notify()在捕获异常时应该返回什么
- 在C++程序中未使用libpq检索NOTIFY
- 如何在Qt中覆盖QApplication::notify
- 等待中的谓词在循环中使用notify时只调用一次
- boost::timed_wait中的Notify条件变量只工作一次
- std::condition_variable wait() and notify_one() synchronizat
- What is boost::program_options::notify() for?
- QThread和notify的实现
- 使用继承信号的Q_PROPERTY NOTIFY的解决方法