std::condition_variable
(C++)
Saw this while working on my SLAM for VR Headset project.
The condition_variable
is used to wake up threads that are waiting on data.
Resources
- https://www.geeksforgeeks.org/thread-synchronization-in-cpp/
- https://www.geeksforgeeks.org/cpp-multithreading-condition-variables/
- https://en.cppreference.com/w/cpp/thread/condition_variable/wait
Include
#include <condition_variable>
Notify One vs. Notify All
notify_one()
wakes up only one of the waiting threads and unblock them (more efficient in scenarios where waking up a single thread is sufficient because it avoids unnecessarily waking up multiple threads)notify_all()
wakes up all threads waiting on the condition variable. This is necessary in situations where multiple threads may be waiting for the same condition and all need to recheck the condition when it changes.
Good to solve the Producer-Consumer Problem.
cv.wait(lock, [] { return data_ready; });
What happens when cv.wait()
is called:
cv.wait
releases the lock and BLOCKS on*this
(source)- After being woken up (through
cv.notify_one()
orcv.notify_all()
on another thread,cv.wait()
checks the condition of second argument (a lambda function returning true or false).- If the predicate returns
true
, the waiting ends, and the thread proceeds with the next steps. Mutex is LOCKED again. - If the predicate returns
false
, the thread goes back to waiting and blocks. Mutex is UNLOCKED.
- If the predicate returns
Why is the mutex needed, isn't the condition variable enough?
No, you need them in conjunction. Imagine a scenario where you have multiple consumers to the same producer. A condition variable is used to make sure that we only consume from the queue when it is non-empty. However, without a
mutex
, after condition variable unblocks, since you’re not using a mutex, the rest of the code doesn’t guarantee that the queue is not empty. You can have a Race Condition again.
mutex mtx;
condition_variable cv;
bool data_ready = false;
void producer() {
// Simulate data production
this_thread::sleep_for(chrono::seconds(2));
// lock release
lock_guard<mutex> lock(mtx);
// variable to avoid spurious wakeup
data_ready = true;
// logging notification to console
cout << "Data Produced!" << endl;
// notify consumer when done
cv.notify_one();
}
void consumer() {
// locking
unique_lock<mutex> lock(mtx);
// waiting
cv.wait(lock, [] { return data_ready; });
cout << "Data consumed!" << endl;
}
// drive code
int main() {
thread consumer_thread(consumer);
thread producer_thread(producer);
consumer_thread.join();
producer_thread.join();
return 0;
}
The second argument explained:
Another example
// condition variable and mutex lock
condition_variable cv;
mutex m;
// shared resource
int val = 0;
void add(int num) {
lock_guard<mutex> lock(m);
val += num;
cout << "After addition: " << val << endl;
cv.notify_one();
}
void sub(int num) {
unique_lock<mutex> ulock(m);
cv.wait(ulock,
[] { return (val != 0) ? true : false; });
if (val >= num) {
val -= num;
cout << "After subtraction: " << val << endl;
}
else {
cout << "Cannot Subtract now!" << endl;
}
cout << "Total number Now: " << val << endl;
}
// driver code
int main()
{
thread t2(sub, 600);
thread t1(add, 900);
t1.join();
t2.join();
return 0;
}
notify_one
vs. notify_all
https://stackoverflow.com/questions/43759609/stdcondition-variablenotify-all-i-need-an-example