之间有什么区别
extern std::atomic<int> x;
int i = x++;
和
extern std::atomic<int> x;
int i = x.fetch_add(1);
我觉得第二个版本更安全,但我看不出这两个版本之间的测试存在任何差异。
答案 0 :(得分:6)
差别绝对不是两种方法都保证的安全性=原子性。
我认为最重要的区别是fetch_add()
可以采用不同的内存顺序参数,而对于增量运算符,它总是memory_order_seq_cst
。
另一个显而易见的区别是,fetch_add()
不仅可以1
作为参数,而另一方面operator++
更有可能使用lock inc
指令实现(尽管fetch_add(1)
理论上,没有什么能阻止编译器对x++
进行这样的优化)
因此,回答您的确切问题,x.fetch_add(1)
和{{1}}之间没有任何语义上的重要区别。 doc says:
此函数的行为就像使用1和memory_order_seq_cst作为参数调用atomic :: fetch_add一样。
答案 1 :(得分:2)
<强> C ++ 11
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澄清了C
和A
:
- a A指的是原子类型之一。
- 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<int>
继承__atomic_base<int>
:
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); }
我不明白为什么postfix会调用fetch_add
帮助器,而且前缀直接使用内置函数,但最后它们都归结为GCC内在函数__atomic_fetch_add
和{{1}这是真正的工作。