我有一个单一生产者、单一消费者用例,其中消费者会阻塞,直到生产者提供新数据可用。 以下是实现此目的的两种同步方法:
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();
}
对我来说,C++20 方法似乎已经完全废弃了旧方法。 有理由仍然使用传统的
std::mutex
和 std::condition_variable
同步吗?
此外,等待
std::atomic_bool
是否与等待 std::condition_variable
一样高效(即没有忙等待)?
也许真正的问题是:为什么 where
wait
调用引入原子?
我在p0514r4中发现了一些潜在的提示:
我们还提出了更简单的原子自由函数,使得 对以原子表示的现有算法进行增量更改,以受益于 信号量背后同样有效的支持
因此,这似乎是通过添加“缺失”的同步功能来“修复”原子 API。另一项为 atomic_flag
添加等待的提案 (
p0995r1) 的内容也是相同的:
我们的经验是,atomic_flag 的接口非常小,几乎没有用处 [...]
我们听说它被用作:
- 一个有问题的自旋环(正如最初的预期);
- “签入”标志,用于了解至少一个线程何时到达程序位置。
由于用户(错误)使用原子标志来同步线程,因此 API 得到增强,为他们提供了比在原子上旋转明显更好的选择。
考虑到这一点,我将回答您的问题如下:
新的原子等待/通知 API 只是 C++ 中的另一个同步 API。我假设选择最有效实现的实现者将在这两种情况下使用相同的底层同步方法。
也就是说,使用
std::mutex
/std::condition_variable
API 的一个重要原因可能是它支持超时等待 (wait_for
/wait_until
),而原子 API 则不支持。