I am not sure if I really understand why std::condition_variable
needs a additional std::mutex
as a parameter? Should it not be locking by its self?
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;
void waits()
{
std::unique_lock<std::mutex> lk(cv_m);
std::cout << "Waiting... \n";
cv.wait(lk, []{return i == 1;});
std::cout << "...finished waiting. i == 1\n";
done = true;
}
void signals()
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Notifying falsely...\n";
cv.notify_one(); // waiting thread is notified with i == 0.
// cv.wait wakes up, checks i, and goes back to waiting
std::unique_lock<std::mutex> lk(cv_m);
i = 1;
while (!done)
{
std::cout << "Notifying true change...\n";
lk.unlock();
cv.notify_one(); // waiting thread is notified with i == 1, cv.wait returns
std::this_thread::sleep_for(std::chrono::seconds(1));
lk.lock();
}
}
int main()
{
std::thread t1(waits), t2(signals);
t1.join();
t2.join();
}
Secondary, in the example they unlock the mutex first (signals
method). Why are they doing this? Shoulden't they first lock and then unlock after notify?
Condition variables are associated with a mutex because it is the only way it can avoid the race that it is designed to avoid.
std::condition_variable The condition_variable class is a synchronization primitive that can be used to block a thread, or multiple threads at the same time, until another thread both modifies a shared variable (the condition), and notifies the condition_variable .
std::mutex The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads.
While mutex implement synchronization by controlling thread access to data, condition variables allow threads to synchronize based upon the actual value of data. Without condition variables, the programmer would need to have threads continually polling (possibly in a critical section), to check if the condition is met.
The mutex protects the predicate, that is, the thing that you are waiting for. Since the thing you are waiting for is, necessarily, shared between threads, it must be protected somehow.
In your example above, i == 1
is the predicate. The mutex protects i
.
It may be helpful to take a step back and think about why we need condition variables. One thread detects some state that prevents it from making forward progress and needs to wait for some other thread to change that state. This detection of state has to take place under a mutex because the state must be shared (otherwise, how could another thread change that state?).
But the thread can't release the mutex and then wait. What if the state changed after the mutex was released but before the thread managed to wait? So you need an atomic "unlock and wait" operation. That's specifically what condition variables provide.
With no mutex, what would they unlock?
The choice of whether to signal the condition variable before or after releasing the lock is a complex one with advantages on both sides. Generally speaking, you will get better performance if you signal while holding the lock.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With