Condition Variables w Predicate
Lost Wakeup
-
The example in the previous lecture has a problem
-
wait()will block until the condition variable is notified -
If the writer calls
notify()before the reader callswait()- The condition variable is notified when there are no threads waiting
- The reader will never be woken up
- The reader could be blocked forever
-
This is known as a "lost wakeup"
Spurious Wakeup
-
Occasionally, the reader will be "spuriously" woken up
- The reader thread has called
wait() - The writing thread has not called
notify() - The condition variable wakes the reader up anyway
- The reader thread has called
-
This is due to the way that
std::condition_variableis implemented- Avoiding spurious wakeups adds too much overhead
-
Fortunately, there is a way to solve both spurious and lost wakeups
wait() with Predicate
-
wait()takes an optional second argument: A predicate -
Typically, the predicate checks a shared bool
- The bool is initialized to false
- It is set to true when the writer sends the notification
-
The reader thread will call this predicate
-
It will only call
wait()if the predicate returns false- Also available for
wait_for()andwait_until()
- Also available for
Using wait() with Predicate
- Add a shared boolean flag, initialized to false
- In the
wait()call, provide a callable object that checks the flag
// bool flag for predicate
bool condition = false;
// Waiting thread
void reader() {
// Lock the mutex
std::unique_lock<std::mutex> uniq_lck(mut);
// Lambda predicate that checks the flag
cond_var.wait(uniq_lck, [] { return condition; });
// ...
}
- In the writer thread, set the flag to true
{
std::lock_guard<std::mutex> lck_guard(mut);
sdata = "Populated";
// Set the flag
condition = true;
}
// notify the condition variable
cv.notify_one();
}
Lost Wakeup Avoidance
-
The writer notifies the condition variable
-
The reader thread locks the mutex
-
The reader thread calls the predicate
-
If the predicate returns true
- Lost wakeup scenario - the writer has already sent a notification
- The reader thread continues, with the mutex locked
-
If the predicate returns false
- Normal scenario
- The reader thread calls
wait()again
Multiple Threads
-
Condition variables are particularly useful here
- Multiple threads are waiting for the same event
-
notify_all()- The condition variable wakes up all the threads which have called
wait() - The threads could wake up in any order
- All the reader threads process the data
- The condition variable wakes up all the threads which have called
-
notify_one()- Only one of the threads which called
wait()will be woken up - The other waiting threads will remain blocked
- A different reader thread processes the data each time
- Only one of the threads which called