简体   繁体   中英

std::condition_variable not properly wakes up after std::condition_variable::notify_all() from other thread

This code is simplification of real project code. Main thread create worker thread and wait with std::condition_variable for worker thread really started. In code below std::condition_variable wakes up after current_thread_state becomes "ThreadState::Stopping" - this is the second notification from worker thread, that is the main thread did not wake up after the first notification, when current_thread_state becomes "ThreadState::Starting". The result was deadlock. Why this happens? Why std::condition_variable not wake up after first 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;
}

Once you call the notify_all method, your main thread and your worker thread (after doing its work) both try to get a lock on the thread_mutex mutex. If your work load is insignificant, like in your example, the worker thread is likely to get the lock before the main thread and sets the state back to ThreadState::Stopped before the main thread ever reads it. This results in a dead lock.

Try adding a significant work load, eg

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

to the worker thread. Dead locks are far less likely now. Of course, this is not a fix for your problem. This is just for illustrating the problem.

You have two threads racing: one writes values of current_thread_state twice, another reads the value of current_thread_state once.

It is indeterminate whether the sequence of events is write-write-read or write-read-write as you expect, both are valid executions of your application.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM