std::atomic<int>:x.fetch_add(1) 和 x++ 之间的区别;

Posted

技术标签:

【中文标题】std::atomic<int>:x.fetch_add(1) 和 x++ 之间的区别;【英文标题】:std::atomic<int>: Difference between x.fetch_add(1) and x++; 【发布时间】:2014-08-15 18:41:42 【问题描述】:

有什么区别

extern std::atomic<int> x;
int i = x++;

extern std::atomic<int> x;
int i = x.fetch_add(1);

我觉得第二个版本更安全,但我看不出这两个版本之间的测试有什么不同。

【问题讨论】:

为了将来的参考,请始终为任何 C++ 包含 [c++] 标记。否则很多潜在的回答者根本看不到它。 【参考方案1】:

区别绝对不在于两种方法都保证的安全性=原子性。

我认为最重要的区别是fetch_add() 可以采用不同的内存顺序参数,而对于增量运算符,它始终是memory_order_seq_cst

另一个明显的区别是fetch_add() 不仅可以将1 作为参数,而另一方面,operator++ 更有可能使用lock inc 指令来实现(尽管理论上没有什么可以阻止编译器这样做) fetch_add(1) 的优化)

所以回答你的确切问题,x++x.fetch_add(1) 之间没有任何语义上的重要区别。 doc says:

这个函数的行为就像 atomic::fetch_add 以 1 和 memory_order_seq_cst 作为参数调用一样。

【讨论】:

x++ 被定义为x.fetch_add(1, std::memory_order_seq_cst) 公平吗? The other doc,供参考。【参考方案2】:

x.fetch_add(1)x++std::atomic 完全相同

如果你相信 cppreference,https://en.cppreference.com/w/cpp/atomic/atomic/operator_arith 说:

T operator++() volatile noexcept; (1)

T* operator++() volatile noexcept; (2)

    执行原子预增量。相当于fetch_add(1)+1

    执行原子后增量。相当于fetch_add(1)

https://en.cppreference.com/w/cpp/atomic/atomic/fetch_add 然后文档:

T fetch_add(T arg, std::memory_order order = std::memory_order_seq_cst ) noexcept;

所以我们看到operator++std::memory_order 默认为std::memory_order_seq_cst,这是可用的更强的,另请参阅:What do each memory_order mean?

C++11 标准引号

如果您不相信 cppreference,C++11 N3337 draft 29.6.5/33 “对原子类型的操作要求”说:

C A ::operator++(int) volatile noexcept;
C A ::operator++(int) noexcept;

返回:fetch_add(1)

29.6.5/2 澄清了CA

A 指的是一种原子类型。 一个 C 指的是它对应的非原子类型

我没能找到它解释清楚,但我想Returns: fetch_add(1) 暗示fetch_add(1) 当然是因为它的副作用而被调用。

还值得进一步研究前缀版本:

C A ::operator++() volatile noexcept;
C A ::operator++() noexcept;

效果:fetch_add(1)

返回:fetch_add(1) + 1

这表示这个返回值 + 1 就像整数的常规前缀增量一样。

GCC 4.8

libstdc++-v3/include/std/atomic 说atomic&lt;int&gt; 继承__atomic_base&lt;int&gt;

struct atomic<int> : __atomic_base<int>

libstdc++-v3/include/bits/atomic_base.h 的实现方式如下:

__int_type
operator++(int) noexcept
 return fetch_add(1); 

__int_type
operator++(int) volatile noexcept
 return fetch_add(1); 

__int_type
operator++() noexcept
 return __atomic_add_fetch(&_M_i, 1, memory_order_seq_cst); 

__int_type
operator++() volatile noexcept
 return __atomic_add_fetch(&_M_i, 1, memory_order_seq_cst); 

_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
 return __atomic_fetch_add(&_M_i, __i, __m); 

_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) volatile noexcept
 return __atomic_fetch_add(&_M_i, __i, __m); 

我不明白为什么后缀调用 fetch_add 帮助器而前缀直接使用内置的,但最后它们都归结为 GCC 内在函数 __atomic_fetch_add__atomic_add_fetch 做真正的工作.

【讨论】:

以上是关于std::atomic<int>:x.fetch_add(1) 和 x++ 之间的区别;的主要内容,如果未能解决你的问题,请参考以下文章

为啥不能交换 std::atomic<T> ?

我可以制作一个线程安全的 std::atomic<vector<int>> 吗?

我可以将 std::atomic<int64> 放在共享内存中并期望原子操作吗?

使用 std::atomic<int> 索引对大型数组进行异步并行化操作有多安全

C++ error: use of deleted function ‘std::atomic<short unsigned int>::atomic(const std::atomic<short

C++ 原子操作 std::atomic<int>