Home > front end >  Does notifying a condition variable guarantee the wake-up of a thread with a successful condition/pr
Does notifying a condition variable guarantee the wake-up of a thread with a successful condition/pr

Time:12-22

The information I've found on cppreference is vague in this regard, so I'm asking here. Say I have two threads waiting on a condition with one having a true predicate, and the other a false one (e.g. condition.wait(lock, [=]{ return some_condition; }). The main thread decides to randomly notify one of them with cond.notify_one().

Assume that the waiting thread selected is the one where the predicate is false. Is the thread going to implicitly notify the next one (if there are any left), or will it be doomed to wait until spurious wakeup?

In case only a single thread is woken up no matter whether its condition succeeds or fails, what would a good way for the first thread to try waking the next one up for a guaranteed successful notify? A naive fix:

condition.wait(lock, [=] {
    if (!some_condition) condition.notify_one();
    return some_condition;
});

Other than pessimization, the "notify wave" may notify the same threads repeatedly which is ineffective never halt in case no threads have successful predicates. A notify_all() won't work, because we may accidentally end waking up multiple threads that satisfy the condition, meanwhile we only want a single one to go through at most.

CodePudding user response:

A notify_all() won't work, because we may accidentally end waking up multiple threads that satisfy the condition, meanwhile we only want a single one to go through at most.

That is not entirely accurate. Only one thread can lock a given mutex at a time, no matter what. If all execution threads who are waiting on the condition variable locked the same mutex (as they should) before they started to wait on the condition variable, then only one of those execution threads will successfully re-lock the mutex and "wake up", and return from wait(). When it unlocks the mutex the next scheduled execution thread will be able to re-lock it and return from its wait(). And so on. notify_all() does not result in all execution threads galloping forward, full speed ahead. Effectively only one thread gets woken up, at a time, because they all must re-lock the same mutex. This single-threads them.

All execution threads get scheduled to be woken up by notify_all, and they will all get woken up. However, effectively, only one execution thread will end up woken first, and lock the mutex. When it unlocks the mutex the next execution thread, that got scheduled to be woken up by notify_all(), will be able to re-lock it, and so on.

Next, let's look at what wait() with a predicate is logically equivalent to:

while (!stop_waiting()) {
    wait(lock);
}

Note that the predicate, here named stop_waiting is checked while the mutex is locked, and it is checked only after the "real" wait(), the one that doesn't check the predicate condition, returns.

Therefore, the solution to your problem is simpler than you think:

  1. Use notify_all().

  2. Whichever thread succeeds in returning from wait() simply needs to do whatever is needed so that the predicate condition is no longer satisfied. The exact manner of doing so depends on the predicate.

So, in the end, one of the execution threads will get woken up, and it will "turn off" the predicate condition. After this execution thread unlocks the mutex, all the other ones will wake up, but the predicate condition is no longer met. The End.

  • Related