【问题标题】:Is std::condition_variable thread-safe?std::condition_variable 是线程安全的吗?
【发布时间】:2018-03-31 01:15:03
【问题描述】:

我有一些这样的代码:

std::queue<myData*> _qDatas;
std::mutex _qDatasMtx;

生成器函数

void generator_thread()
{
  std::this_thread::sleep_for(std::chrono::milliseconds(1));
  {
    std::lock_guard<std::mutex> lck(_qDatasMtx);
    _qData.push(new myData);
  }
  //std::lock_guard<std::mutex> lck(cvMtx); //need lock here?
  cv.notify_one();
}

消费者函数

void consumer_thread()
{
  for(;;)
  {
    std::unique_lock lck(_qDatasMtx);
    if(_qDatas.size() > 0)
    {
       delete _qDatas.front();
       _qDatas.pop();
    }
    else
      cv.wait(lck);
  }
}

如果我有几十个生成器线程和一个消费者线程,在每个线程中调用 cv.notify_one() 时是否需要互斥锁?

std::condition_variable 是线程安全的吗?

【问题讨论】:

  • 为什么不std::queue&lt;std::unique_ptr&lt;myData&gt;&gt;? Raw new/delete 是 c++ 世界中最可怕的事情之一。

标签: c++ multithreading c++11


【解决方案1】:

notify_one 可以不加锁地从多个线程调用。

但是,为了使正常操作正常工作并且没有通知“溜走”的机会,您必须在修改虚假唤醒保护和/或打包条件变量的点之间保持条件变量受保护的锁定检查已读,以及您通知的点。

您的代码似乎通过了该测试。不过这个版本更清晰:

std::unique_lock lck(_qDatasMtx);
for(;;) {
  cv.wait(lck,[&]{ return _qDatas.size()>0; });
  delete _qDatas.front();
  _qDatas.pop();
}

它减少了虚假的解锁/重新锁定。

【讨论】:

    【解决方案2】:

    在每个线程中调用cv.notify_one() 时是否需要mutex 锁?

    没有

    std::condition_variable 线程安全吗?

    是的


    调用wait 时,您传递了一个锁定的mutex,该mutex 会立即解锁以供使用。调用notify 时,您不会使用相同的mutex 锁定它,因为会发生什么(在链接中有详细说明):

    1. 通知
    2. 正在等待的唤醒线程
    3. 锁定互斥体以供使用

    来自std::condition_variable

    在 std::condition_variable 上执行 notify_one 或 notify_all(通知不需要持有锁)

    来自std::condition_variable::notify_all

    通知线程不需要持有与等待线程持有的互斥锁相同的互斥锁;


    关于您的代码 sn-p:

    //std::lock_guard<std::mutex> lck(cvMtx); //need lock here?
    cv.notify_one();
    

    不,那里不需要锁。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-23
      • 2017-12-23
      • 2017-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多