为什么不使用 std::memory_order_acq_rel

Why not use std::memory_order_acq_rel

最近在学习cppcoro项目的代码。我有一个问题。

https://github.com/lewissbaker/cppcoro/blob/master/lib/async_auto_reset_event.cpp#L218 https://github.com/lewissbaker/cppcoro/blob/master/lib/async_auto_reset_event.cpp#L284

if (waiter->m_refCount.fetch_sub(1, std::memory_order_release) == 1) // #218
{
    waiter->m_awaiter.resume();
}

using memory_order_release写在第218行,m_refCountusing memory_order_acquire标志可以在第284行正确加载值。这就可以了。但是 fetch_sub 是一个 RMW 操作。要正确读取第284行的修改,是否还需要一个memory_order_aquire标志?所以我想知道为什么 m_refCount 在第 218 行和第 284 行中不使用 memory_order_acq_rel?

return m_refCount.fetch_sub(1, std::memory_order_acquire) != 1; // #284

谢谢。

无论哪种方式,对原子变量本身的操作都不会导致数据竞争。

std::memory_order_release 表示所有修改过的缓存数据都将提交到共享内存/RAM。内存顺序操作生成内存栅栏,因此其他对象可以正确地提交到共享内存/从共享内存读取。

因为这不是内存指令的工作方式。

我们为我们的原子操作添加一个内存屏障以实现两件事:

  1. 以防止松散的原子操作和非原子操作被重新排序。
  2. 跨线程同步非原子数据

我写了一个回答,把这两点解释的比较清楚

当协程在一个线程中暂停,并在另一个线程中恢复时,不需要额外的同步*,cppreference on coroutines 说:

Note that because the coroutine is fully suspended before entering awaiter.await_suspend(), that function is free to transfer the coroutine handle across threads, with no additional synchronization.

至于重新排序?实际逻辑 ( waiter->m_awaiter.resume();) 被一大块 if 语句包围。无论如何,编译器无法在 fetch_sub 之前重新排序恢复,因为它会忽略 if 语句的作用并破坏代码的逻辑。

所以,我们这里不需要任何其他的内存顺序,而是放松。 fetch_XXX 是一个 RMW 操作这一事实没有任何意义 - 我们为正确的用例使用正确的内存顺序。

如果你喜欢cppcoro,请试试我自己的协程库,concurrencpp

*更正确的说法是:不需要额外的同步除了为了从一个线程传递coroutine_handle所需的同步 给另一个。