Is that so? I'm not sure. Suppose there is one blocked consumer thread
because the queue is empty. Then, the producer enters the critical
section to push an item onto the queue. Then, a second consumer that
wasn't blocked tries to enter the critical section. The producer
invokes not_empty_condition.notify_one() to wake up a potentially
blocked consumer and then unlocks the mutex. Is it guaranteed that the
previously blocked consumer thread will be the first to lock the mutex
or could the 2nd consumer thread win the race?
I was about to write a response similar to yours and that if the
notification is kept inside the critical section it should be fine to
replace the consumer's while loop
std::unique_lock<std::mutex> lck { mut };
while (qi.empty()) {
not_empty_condition.wait(lck);
}
assert(!qi.empty()); // Obviously!
to a simple if:
std::unique_lock<std::mutex> lck { mut };
if (qi.empty()) {
not_empty_condition.wait(lck);
}
assert(!qi.empty()); // True?!
But I'm not so sure about that anymore. Even if the notification is
done while the mutex was locked, another consumer could still win the
race to lock the mutex and "steal" the item off the queue, couldn't
it?
I'd recommend to stick with a while loop to be on the safe side
(protecting yourself from spurious awakenings). And once you use while
loops or the equivalent
std::unique_lock<std::mutex> lck { mut };
not_empty_condition.wait(lck, [&]{ return !qi.empty(); });
assert(!qi.empty()); // Obviously
you could just as well do the notification outside of the critical
section. Whether that makes a difference, I don't know.
Cheers!
SG