如何使用 fetch_sub 和 atomic_thread_fence 减少多线程?
Posted
技术标签:
【中文标题】如何使用 fetch_sub 和 atomic_thread_fence 减少多线程?【英文标题】:how to decrement mutithreaded using fetch_sub and atomic_thread_fence? 【发布时间】:2019-09-03 08:47:21 【问题描述】:我们有一个成员方法 (bool try()),它应该是线程安全的,如果变量 m_count 大于 0,它会递减它,我们尽量避免互斥体,而是使用 fetch_sub 和 atomic_thread_fence。
struct Test
bool try()
if (m_count<1)
return false;
int count = m_count.fetch_sub(1, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
return true;
Test():m_count(1)
private:
std::atomic<int> m_count;
我们希望确保 m_count 永远不会小于 0,并且如果 m_count 减少,则 try 返回 true。以上两个线程可以将 m_count 从 1 递减到 -1,这是不可接受的。
【问题讨论】:
为什么先做轻松操作,再做内存栅栏?为什么不直接使用想要的内存顺序呢? 【参考方案1】:加载if (m_count<1)
和调用fetch_sub()
之间存在间隙。
假设m_count == 1
,一个线程执行加载并继续,但在它执行fetch_sub()
之前,第二个线程执行加载并获得相同的值(1
)。现在两个线程都将执行fetch_sub()
并且m_count
变为-1
。
为了消除这种差距,您可以将比较和修改组合成一个原子比较和交换 (CAS) 操作,如下所示:
bool do_try()
bool modified=false;
int current = m_count.load(std::memory_order_relaxed);
do
if (current == 0)
break;
assert(current > 0);
while (!(modified = m_count.compare_exchange_weak(current, current-1, std::memory_order_relaxed)));
std::atomic_thread_fence(std::memory_order_acquire);
return modified;
现在m_count
不能变成-1
。
如果compare_exchange()
返回false
,它会将其第一个参数更新为最新值,因此您不必再次调用load()
。
【讨论】:
这可以在compare_exchange_weak
上使用memory_order_acquire
并完全删除atomic_thread_fence
吗?
@MaximEgorushkin 是的,但如果你还必须在load
上使用它,那就太悲观了(在弱平台上可能会导致 2 个围栏指令)
为什么load
需要使用memory_order_acquire
?
@MaximEgorushkin 这取决于调用它的代码是什么样的。以上是关于如何使用 fetch_sub 和 atomic_thread_fence 减少多线程?的主要内容,如果未能解决你的问题,请参考以下文章