I am reading the example code of using condition_variable here. I post the code below:
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::cout << "------------------------\n";
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
return 0;
}
My question is the worker_thread is launched first, so I would assume the mutex m is locked by the worker_thread, but why in the main the mutex m still can be locked by lock_guard?
A condition variable is only one part of a tripod.
The three parts are the condition variable, state and the mutex that guards the state.
The condition variable provides a mechanism to notify when the state changes.
This operation uses all 3:
cv.wait(lk, []{return ready;})
The condition variable's method takes a lock (which must have been acquired), and a lambda (which tests the state).
Within the wait method, the lk is unlocked until the condition variable detects a message (which could be spurious). When it detects a message, it relocks the mutex and runs the test (whose goal is to determine if the detection was spurious). If the test fails, it unlocks and waits again: if the test passes, it keeps the lock locked and exits.
There is also the "the test threw" path, which results in a different lock state depending on the version of the standard your code implemented (C++11 had a defect, IIRC).
The important thing you missed is that wait unlocks the mutex passed in.
Related
See the following classical producer-consumer code:
int main()
{
std::queue<int> produced_nums;
std::mutex m;
std::condition_variable cond_var;
bool done = false;
bool notified = false;
std::thread producer([&]() {
for (int i = 0; i < 5; ++i) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::unique_lock<std::mutex> lock(m);
std::cout << "producing " << i << '\n';
produced_nums.push(i);
notified = true;
cond_var.notify_one();
}
done = true;
cond_var.notify_one();
});
std::thread consumer([&]() {
std::unique_lock<std::mutex> lock(m);
while (!done) {
while (!notified) { // loop to avoid spurious wakeups
cond_var.wait(lock);
}
while (!produced_nums.empty()) {
std::cout << "consuming " << produced_nums.front() << '\n';
produced_nums.pop();
}
notified = false;
}
});
producer.join();
consumer.join();
}
I have copied this from cppreference.
Everything is pretty much straightforward to me, except the line in the consumer:
cond_var.wait(lock);
I do understand the loop that waits cond_var to be notified, but why is it waiting for the lock?
cond_var.wait(lock); does not wait for the lock.
That line does 3 things
It unlocks the lock variable
It waits until someone signals the condition.
it locks the lock variable again before it returns,
It does all this atomically. While the thread is waiting for the condition variable, the mutex is not locked - that way your producer thread can acquire the lock and safely set any variables shared between the consumers/producer.
It locks the mutex again upon return, so the consumer again can safely access the shared variables.
If you tried to manage locking/unlocking the mutex yourself, you would end up with race conditions betwen locking/unlocking the mutex and waiting/signalling the condition variable - this is why waiting for a condition variable is tied to a mutex - so it can be done atomically, without race conditions.
Typical usages of condition variables look like this (see code below): http://en.cppreference.com/w/cpp/thread/condition_variable.
However, it seems that the main thread could potentially call notify_one before the worker thread calls wait, which would result in a deadlock. Am I mistaken? If not, what is the usual workaround for this?
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
}
Note the definition of wait that uses a condition (the only wait you should ever be using):
while (!pred()) {
wait(lock);
}
if notify was already fired it means the condition is already true (that was sequenced before notify_one in the signalling thread). So when the receiver takes the mutex and looks at pred(), it will be true and it will proceed.
Basically what i am trying to achieve is to check whether the data has been changed or not since the last time i checked.
What i am doing here is i initiate a separate thread that continuously runs in a loop and checks the stop variable at the end of the loop. The stop variable is a global variable so i can easily give it a 0 value to terminate the polling loop from the main thread.
In the loop i have a set of variables that hold the value of data that i retrieved in the previous iteration and a set of variables that are used to store the recently retrieved data. All i do is compare the variable with the new data with the ones that are holding the previous data. After this i update the set of variables that are holding the previous data to the most recent data.
I wanted to ask if there is a more efficient way of doing this? maybe something that would not require polling?
Yes; one way is to have the polling thread wait on a condition variable, and have the producer wake it up by signalling the same condition variable.
An example in C++ is given at cppreference:
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
}
I am studying this example. I have found this question and thought that I will get an answer, but I still have a question.
I post the the code here for convenience:
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::cout << "------------------------\n";
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
return 0;
}
Should not the statement std::unique_lock<std::mutex> lk(m); block the main thread because mutex m is locked by worker_thread? If yes, isn't the statement cv.wait(lk, []{return processed;}); after it unnecessary in this example? When main thread can lock the mutex, processed will be already true.
The call to wait unlocks the mutex for the duration of the wait. See http://en.cppreference.com/w/cpp/thread/condition_variable/wait.
EDIT: Which is explicitly stated in the answer to the question you linked to: https://stackoverflow.com/a/32030975/212870
EDIT 2: It is not true that "When main thread can lock the mutex, processed will already be true". The worker thread may not even have started yet, or if it has it may not have seen that ready is set.
Line cv.wait(lk, []{return ready;}); does the following if ready is false:
Unlocks the mutex lk
Blocks the thread waiting for notification
When notification arrives, unblocks the thread and locks the mutex lk
So the main thread does not block on std::lock_guard<std::mutex> lk(m); as the mutex is unlocked by worker thread.
I am trying to understand what happens to a mutex when it is used in a condition variable.
In the following example, taken from cppreference
int main()
{
std::queue<int> produced_nums;
std::mutex m;
std::condition_variable cond_var;
bool done = false;
bool notified = false;
std::thread producer([&]() {
for (int i = 0; i < 5; ++i) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::unique_lock<std::mutex> lock(m);
std::cout << "producing " << i << '\n';
produced_nums.push(i);
notified = true;
cond_var.notify_one();
}
done = true;
cond_var.notify_one();
});
std::thread consumer([&]() {
std::unique_lock<std::mutex> lock(m);
while (!done) {
while (!notified) { // loop to avoid spurious wakeups
cond_var.wait(lock);
}
while (!produced_nums.empty()) {
std::cout << "consuming " << produced_nums.front() << '\n';
produced_nums.pop();
}
notified = false;
}
});
producer.join();
consumer.join();
}
The producer thread calls cond_var.notify_one() before the mutex gets unlocked. Does the mutex m get unlocked when notify is called, or does the notification occurs only when the mutex gets unlocked?
Notifying does not unlock the mutex. You can tell (indirectly) because you don't pass the lock to notify_one() the way you do to wait(), which does release the mutex while it waits.
On the other side, the notified thread(s) are notified "immediately". But they won't necessarily return from wait() immediately. Before they can return from wait() they must first re-acquire the mutex, so they will block there until the notifying thread releases it.
The lock is being acquired in the constructor and released in the destructor of std::unique_lock. From this info you can deduce that the producer releases the lock after the call to notify_one() completes.
For performance reason I suggest to unlock the mutex before to notify other threads.