简体   繁体   中英

C++ condition_variable wait_for returns instantly

This is simple code from http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/

Why does wait_for() return instantly if I comment line with starting thread?

Like this:

// condition_variable::wait_for example
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <chrono>             // std::chrono::seconds
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable, std::cv_status

std::condition_variable cv;

int value;

void read_value() {
  std::cin >> value;
  cv.notify_one();
}

int main ()
{
  std::cout << "Please, enter an integer (I'll be printing dots): ";
  //std::thread th (read_value);

  std::mutex mtx;
  std::unique_lock<std::mutex> lck(mtx);
  while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
    std::cout << '.';
  }
  std::cout << "You entered: " << value << '\n';

  //th.join();

  return 0;
}

Update:

Please don't look for other problems in this example (related buffering cout ...). The original question was about why wait_for is skipped.

Short answer: compile with -pthread and your issue will go away.

Update:

This is a confirmed bug/issue in libstdc++. Without -pthread being passed in as a compiler flag, the timed wait call will return immediately. Given the history of the issue (3 years), it's not likely to be fixed anytime soon. Anyway, read my message below on why you should be using condition variables with predicates to avoid the spurious wakeup problem. It still holds true even if you are linking with the posix threads library.

That sample code on cplusplus.com has several issues. For starters, amend this line:

std::cout << '.';

To be like this:

std::cout << '.';
std::cout.flush()

Otherwise, you won't see any dots if stdout isn't getting flushed.

If you compile your program (with the thread commented out) like this:

g++ yourcode.cpp -std=c++11

Then the resulting a.out program exhibits the issue you described when the thread is not used. That is, there's a spurious wakeup when the thread is not used. It's like there's a phantom notify() call being invoked on the condition variable from some unknown source. This is odd, but not impossible.

But as soon as you uncomment out the declaration of the thread variable, the program will throw an exception (and crash) as a result of the program not using a multithreaded:

terminate called after throwing an instance of 'std::system_error'
  what():  Enable multithreading to use std::thread: Operation not permitted
Please, enter an integer (I'll be printing dots): Aborted (core dumped)

Interesting, so let's fix that by recompiling with -pthread

g++ yourcode.cpp -std=c++11 -pthread

Now everything works as expected with or without the thread. No more spurious wakeup it seems.

Now let's talk about why you are seeing the behavior you are seeing. Programs using condition variables should always be written to deal with spurious wakeup. And preferably, use a predicate statement. That is, you might get a phantom notify causing your wait or wait_for statement to return early. The example code on the web from cplusplus.com doesn't use a predicate nor does it deal with this possibility.

Let's amend it as follows:

Change this block of code:

  while (cv.wait_for(lck,std::chrono::seconds(1))==std::cv_status::timeout) {
    std::cout << '.';
  }

To be this:

 while (cv.wait_for(lck,std::chrono::seconds(1), condition_check)==false) {
   std::cout << '.';
   std::cout.flush();
 }

And then elsewhere outside of main , but after the declaration of value , add this function:

bool condition_check() {
   return (value != 0);
}

Now the wait loop will wake up every second and/or when the notify call is made by the input thread. The wait loop will continue until value != 0 . (Technically, value should be synchronized between threads, either with the lock or as a std::atomic value, but that's a minor detail).

Now the mystery is why does the non-predicate version of wait_for suffer from the spurious wake_up problem. My guess is that's an issue with the single threaded C++ runtime that goes away when the multithreaded runtime ( -pthread ) is used. Perhaps condition_variable has different behavior or a different implementation when the posix thread library is linked in.

There are several issue with this code:

First, as you have noticed, the program has to be build with the -pthread option.

Second, you need to flush the output if you want to see the dots printed.

Most importantly, this is entirely incorrect usage of mutex and condition variable. A condition variable notification indicates a change of value in a user-specified predicate/condition: the changing of the condition and examining it must be atomic and serialized: otherwise there is a data race and the behavior of the program would be undefined.

As is the case with the example program: value is read and written by two threads, but without any concurrency control mechanism, or to put it differently, there's no "happens-before" relation between the operation, which reads value and the operation which writes value .

Fixed example follows:

// condition_variable::wait_for example
#include <chrono>             // std::chrono::seconds
#include <condition_variable> // std::condition_variable, std::cv_status
#include <iostream>           // std::cout
#include <mutex>              // std::mutex, std::unique_lock
#include <thread>             // std::thread

std::mutex mtx;
std::condition_variable cv;

int value;

void read_value() {
  int v;
  std::cin >> v;
  std::unique_lock<std::mutex> lck(mtx);
  value = v;
  cv.notify_one();
}

int main() {
  std::cout << "Please, enter an integer (I'll be printing dots): ";
  std::thread th(read_value);

  std::unique_lock<std::mutex> lck(mtx);
  while (cv.wait_for(lck, std::chrono::seconds(1)) == std::cv_status::timeout) {
    std::cout << '.' << std::flush;
  }
  std::cout << "You entered: " << value << '\n';

  th.join();

  return 0;
}

So, what are the changes:

  • mutex is moved to global scope (for the sake of the example), so the thread, which reads value can lock it, in order to modify value .
  • the read is in a separate variable; it cannot be directly into value , because value must be modified only under the protection of the mutex, but holding the mutex, while waiting from input form std::cin would prevent the main thread from printing dots, as it will try to acquire the mutex upon timeout.
  • after each dot output, the std::cout is flushed

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