Reputation: 11
I'm new to condition variables and I wonder why this piece of code blocks after counter variable is equal to 99? Deleting the for loop and putting "counter += 99" instead makes the code work, does it have anything to do with sleep_for? Thanks for help :)
#include<thread>
#include<condition_variable>
#include<mutex>
#include<chrono>
#include <iostream>
std::condition_variable cv;
std::mutex mtx;
int counter = 0;
void foo() {
std:: unique_lock<std::mutex>lck{ mtx };
//counter += 99;
for (; counter < 100; counter++) {
std::cout << counter << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
lck.unlock();
cv.notify_one();
}
int main() {
std::thread th(&foo);
{
std::unique_lock<std::mutex>lck{ mtx };
cv.wait(lck, [] {
return counter == 99;
});
}
std::cout << "!!!!!" << std::endl;
th.join();
}
Upvotes: 0
Views: 162
Reputation: 3249
#include<thread>
#include<condition_variable>
#include<mutex>
#include<chrono>
#include <iostream>
std::condition_variable cv;
std::mutex mtx;
int counter = 0;
void foo() {
std:: unique_lock<std::mutex>lck{ mtx };
//counter += 99;
for (; counter < 100; counter++) { // B
std::cout << counter << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
lck.unlock();
cv.notify_one(); // C
}
At the end of your loop (B), counter has a value of 100.
int main() {
std::thread th(&foo);
{
std::unique_lock<std::mutex>lck{ mtx };
cv.wait(lck, [] {
return counter == 99; // A
});
}
std::cout << "!!!!!" << std::endl;
th.join();
}
Your condition to wake up the thread running main()
is that counter
has a value of 99 (A).
Because you call unlock()
after your loop is finished (C), the value of counter
is 100. Your std::condition_variable
will only wake up its the waiting thread when the value of counter
is 99 (A). Therefore, unless there is a spurious wakeup in the waiting thread when counter
has a value of 99 (which would cause the std::condition_variable
's condition to evaluate to true
), your waiting thread will be stuck waiting forever.
You can fix this by simply changing the std::condition_variable
's predicate as follows:
{
std::unique_lock<std::mutex>lck{ mtx };
cv.wait(lck, [] {
return counter == 100; // CHANGED TO 100 FROM 99.
});
}
Or (the exclusive kind) by changing the condition in your for()
loop as follows:
for (; counter < 99; counter++) { // `counter < 100` is now `counter < 99`
std::cout << counter << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
Upvotes: 0
Reputation: 303357
Let's add some annotation to your code:
void foo() {
std:: unique_lock<std::mutex>lck{ mtx };
// counter == 0
for (; counter < 100; counter++) {
...
}
// counter == 100
lck.unlock();
cv.notify_one(); // <== notify here
}
We're looping until counter == 100
, at which point we notify cv
. However, we are waiting for counter == 99
, which is not true at the point when it gets notified. The only way for wait()
to return in your code is for a spurious wakeup to happen right at the last iteration of the loop.
Perhaps you meant to loop while counter < 99
.
Upvotes: 1