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:
New C++20 approach using 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();
}
Traditional C++17 approach using 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();
}
Question
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
waitcalls on atomics introduced?I found some potential hints about that in p0514r4:
So this seems about 'fixing' the atomic API by adding a 'missing' synchronization feature. Another proposal (p0995r1) for adding wait to
atomic_flagreads along the same lines: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_variableAPI could be, that it supports waits with timeout (wait_for/wait_until), whereas the atomic API does not.