user90843
user90843

Reputation:

common use case scenarios for condition_variable

A condition variable can be used to signal to other threads that something has happened:

mutex m;
condition_variable cv;

thread t1([&cv]{
    // processing
    ...
    cv.notify_one();
});
...
unique_lock<std::mutex> lck(m);
cv.wait(lck);

But as you can see, there is a window of opportunity that the thread processing is finished and the notification is passing by before we get to wait to be notified, so we will wait forever.

In that case, a common solution is the use of a flag:

mutex m;
condition_variable cv;
bool done = false;

thread t1([&cv,&done]{
    // processing
    ...
    done = true; 
    cv.notify_one();
});
...
unique_lock<std::mutex> lck(m);
cv.wait(lck, [&done]{return done;});

Is using a flag the common way to use a condition_variable, or is my interpretation wrong?

Upvotes: 5

Views: 4708

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171383

A condition variable should always be associated with some condition, which you should test:

    unique_lock<mutex> lck(m);
    while (!something) 
      cv.wait(lck);

The condition is checked while the mutex is held, so that implies the mutex should protect the data associated with the condition, so you know it won't change between testing it and waiting.

The test is a while not just an if because some condition variable implementations (including pthreads-based ones) may wake up spuriously i.e. when noone signalled it, so you should check the condition in a loop and re-wait while it isn't true. There's an overload of wait that takes a predicate and automatically handles spurious wake ups by waiting until the predicate returns true, e.g. here's the above example modified to use a lambda that checks the condition:

    unique_lock<mutex> lck(m);
    cv.wait(lck, [&] { return something; });

(In simple cases I find the explicit while loop easier to read.)

A condition variable that is in use can be thought of as a 3-tuple consisting of the condition variable, a mutex and a predicate, which are conceptually bound together by being used together to wait on the condition variable. All concurrent waits on a particular condition variable object must use the same mutex, and will generally also be waiting on the same predicate (or a related predicate that depends on the same data, protected by the same mutex.)

Upvotes: 8

Usually condition_variable variables are employed in scenarios where one thread detects that it cannot proceed and decides to wait until some condition (in the English sense) is met. The condition_variable itself is not the main mechanism to notify that the thread should proceed but rather that if any thread is waiting it should recheck as the state might have changed and it might be fine to proceed now.

One of the simplest examples is a producer/consumer queue, where the consumer will have code like:

void consume() {
   empty.wait( [&] { return !queue.empty(); } );
   // extract data from the queue and consume it here
}

That is, the thread is not just waiting on the condition_variable variable, but rather waiting until the state of the object is such that the thread can proceed. Similarly, notifying a condition_variable is not telling another thread to continue, just notifying any thread waiting for a condition to be met to retest because the state might have changed.

Going back to your use case, if the condition that needs to be met for your thread to continue is that the other thread is done, then using such a flag is perfectly fine.

Upvotes: 0

Related Questions