I have a single producer, single consumer use case where the consumer blocks until the producer has made new data available. Here are two synchronization approaches that implement this:
std:atomic<bool>::wait
data d;
std::atomic<bool> ready = false;
void consume_bool() {
ready.wait(false);
do_consume(d);
ready.store(false);
ready.notify_one();
}
void produce_bool() {
ready.wait(true);
d = do_produce();
ready.store(true);
ready.notify_one();
}
std::condition_variable
data d;
std::mutex m;
std::condition_variable consume_ready, produce_ready;
bool ready = false;
void consume_cv() {
std::unique_lock lock{m};
consume_ready.wait(m, [] { return ready; });
do_consume(m);
ready.store(false);
produce_ready.notify_one();
}
void produce_cv() {
std::unique_lock lock{m};
produce_ready.wait(m, [] { return !ready; });
d = do_produce();
ready.store(true);
consume_ready.notify_one();
}
To me, it seems like the C++20 approach has entirely obsoleted the old approach.
Is there a reason to still use traditional std::mutex
and std::condition_variable
synchronization?
Also, is waiting with std::atomic_bool
as efficient (i.e. no busy-wait) as waiting for a std::condition_variable
?
Maybe the question really is: Why where wait
calls on atomics introduced?
I found some potential hints about that in p0514r4:
we also propose simpler atomic free functions that enable incremental change to pre-existing algorithms expressed in terms of atomics, to benefit from the same efficient support behind semaphores
So this seems about 'fixing' the atomic API by adding a 'missing' synchronization feature. Another proposal (p0995r1) for adding wait to atomic_flag
reads along the same lines:
Our experience is that atomic_flag's interface is so minimal as to be mostly useless [...]
We’ve heard of it being used as:
- A questionable spinloop (as was originally intended);
- A "check-in" flag used to know when at least one thread has reached a program location.
Because users are (mis)using atomic flags for synchronizing threads, the API is enhanced to give them obvious better choices than spinning on the atomic.
From experience it also seems to me that people just understand atomics really fast compared to the mutex + condition_variable + lock + semaphore + latch + barrier mess that the C++ standard library provides. Therefore enhancing that simple API with some useful functionality makes some sense to me.
With that in mind I would answer your question as follows:
The new atomic wait/notify API is just another synchronization API in C++. I would assume that implementators choosing the most efficient implementation will use the same underlying synchronization method in both cases.
That beeing said, one big reason to use the std::mutex
/std::condition_variable
API could be, that it supports waits with timeout (wait_for
/wait_until
), whereas the atomic API does not.