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

how to decrement mutithreaded using fetch_sub and atomic_thread_fence?

我们有一个成员方法 (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 递减则尝试 returns 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() returns false,它会用最新值更新它的第一个参数,所以你不必再次调用load()