为什么不使用 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。内存顺序操作生成内存栅栏,因此其他对象可以正确地提交到共享内存/从共享内存读取。
因为这不是内存指令的工作方式。
我们为我们的原子操作添加一个内存屏障以实现两件事:
- 以防止松散的原子操作和非原子操作被重新排序。
- 跨线程同步非原子数据
我写了一个回答,把这两点解释的比较清楚
当协程在一个线程中暂停,并在另一个线程中恢复时,不需要额外的同步*,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
所需的同步
给另一个。
最近在学习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。内存顺序操作生成内存栅栏,因此其他对象可以正确地提交到共享内存/从共享内存读取。
因为这不是内存指令的工作方式。
我们为我们的原子操作添加一个内存屏障以实现两件事:
- 以防止松散的原子操作和非原子操作被重新排序。
- 跨线程同步非原子数据
我写了一个回答
当协程在一个线程中暂停,并在另一个线程中恢复时,不需要额外的同步*,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
所需的同步
给另一个。