如何使用 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()
。
我们有一个成员方法 (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()
。