如何使用fetch_sub和atomic_thread_fence递减多线程?

how to decrement mutithreaded using fetch_sub and atomic_thread_fence?

本文关键字:fence thread 多线程 atomic 何使用 fetch sub      更新时间:2023-10-16

我们有一个成员方法(bool try(((,它应该是线程安全的,如果变量大于0,它会递减变量m_count,我们尽量避免互斥锁,而是使用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,并且如果 try 递减m_count则返回 true。上面的两个线程可以将m_count从 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()