std :: condition_variable在其他线程的std :: condition_variable :: notify_all()之后没有正确唤醒

时间:2015-08-03 14:06:38

标签: c++ condition-variable wakeup

此代码简化了实际项目代码。主线程创建工作线程并等待std :: condition_variable工作线程真正启动。在下面的代码中,std :: condition_variable在current_thread_state变为" ThreadState :: Stopping"之后醒来。 - 这是来自工作线程的第二个通知,即当第一个通知后,当current_thread_state变为" ThreadState :: Starting"时,主线程没有被唤醒。结果是僵局。为什么会这样?为什么std :: condition_variable在第一次thread_event.notify_all()之后没有被唤醒?

int main()
{
  std::thread thread_var;
  struct ThreadState {
    enum Type { Stopped, Started, Stopping };
  };
  ThreadState::Type current_thread_state = ThreadState::Stopped;
  std::mutex thread_mutex;
  std::condition_variable thread_event;
  while (true) {
    {
      std::unique_lock<std::mutex> lck(thread_mutex);
      thread_var = std::move(std::thread([&]() {
        {
          std::unique_lock<std::mutex> lck(thread_mutex);
          cout << "ThreadFunction() - step 1\n";
          current_thread_state = ThreadState::Started;
        }
        thread_event.notify_all();

        // This code need to disable output to console (simulate some work).
        cout.setstate(std::ios::failbit);
        cout << "ThreadFunction() - step 1 -> step 2\n";
        cout.clear();

        {
          std::unique_lock<std::mutex> lck(thread_mutex);
          cout << "ThreadFunction() - step 2\n";
          current_thread_state = ThreadState::Stopping;
        }
        thread_event.notify_all();
      }));

      while (current_thread_state != ThreadState::Started) {
        thread_event.wait(lck);
      }
    }

    if (thread_var.joinable()) {
      thread_var.join();
      current_thread_state = ThreadState::Stopped;
    }
  }
  return 0;
}

2 个答案:

答案 0 :(得分:2)

一旦调用notify_all方法,主线程和工作线程(完成工作后)都试图锁定thread_mutex互斥锁。如果您的工作负载无关紧要,例如在您的示例中,工作线程可能会在主线程之前获取锁定,并在主线程读取之前将状态设置回ThreadState::Stopped。这导致死锁。

尝试添加重要的工作量,例如

std::this_thread::sleep_for( std::chrono::seconds( 1 ) );

到工作线程。死锁现在不太可能。当然,这不是解决您的问题的方法。这只是为了说明问题。

答案 1 :(得分:0)

你有两个线程竞争:一个写入current_thread_state两次的值,另一个读取current_thread_state的值一次。

事件序列是否是您所期望的写 - 读 - 读或写 - 读 - 写是不确定的,两者都是您的应用程序的有效执行。