条件变量信号没有保持锁定

时间:2014-01-29 18:20:08

标签: c++ multithreading c++11 condition-variable

所以我发现,如果你没有在c ++ 11中持有锁,那么发出一个条件变量是合法的。这似乎打开了一些令人讨厌的竞争条件的大门:

std::mutex m_mutex;
std::condition_variable m_cv;

T1: 
  std::unique_lock<std::mutex> lock(m_mutex);
  m_cv.wait(lock, []{ return !is_empty(); });

T2:
  generate_data();
  m_cv.notify();

是否保证T1永远不会在我们首先检查is_empty()(它返回true)的情况下结束,然后被T2抢占,这会创建一些数据并在我们真正等待它之前发出条件变量的信号?

如果这可以保证有效(我猜是这样,否则看起来似乎是一个故意错误的API设计),对于linux和stdlibc++来说,这实际上是如何实现的?似乎我们需要另一个锁来避免这种情况。

2 个答案:

答案 0 :(得分:7)

检查谓词和等待不会在std::condition_variable::wait中以原子方式执行(解锁锁定和休眠 以原子方式执行)。如果另一个线程可以在此线程持有互斥锁时更改谓词的值,则可以在谓词检查和进入睡眠之间进行通知,并且实际上会丢失。

在您的示例中,如果generate_data()中的T2可以在不保留is_empty()的情况下更改m_mutex的结果,则T1之间可能会发生通知检查is_empty()并在m_cv上休息。在更改谓词和通知之间的任何时间保持互斥锁都足以保证谓词检查的原子性和另一个线程中的wait调用。这可能看起来像:

{
  std::lock_guard<std::mutex> lk(m_mutex);
  generate_data();
}
m_cv.notify();

甚至

generate_data();
std::lock_guard<std::mutex>(m_mutex); // Lock the mutex and drop it immediately
m_cv.notify();

答案 1 :(得分:3)

无法保证 - 如果您不想错过信号,则必须在通知之前锁定互斥锁。某些应用程序可能对缺失信号不可知。

来自man pthread_signal:

线程可以调用pthread_cond_signal()或pthread_cond_broadcast()函数,无论它当前是否拥有调用pthread_cond_wait()或pthread_cond_timedwait()的线程在等待期间与条件变量相关联的互斥锁;但是,如果需要可预测的调度行为,那么调用pthread_cond_signal()或pthread_cond_broadcast()的线程会锁定该互斥锁。