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);
in fact releases the mutexm
locked in the constructor oflock
before waiting. Then, if the consumer thread gets notified,m
is locked again internally, so the thread can safely useproduced_nums
container. – Randolfdone
can be set without protection,The last iteration sets notified and signals, then releases the mutex. then the consumer picks up the mutex, eventually clears notified, circles around to check done (which may still not be set by the producer, but is about to be, unprotected), then falls back into waiting for anotified
that will never come. – Chincapin