之间有什么区别
extern std::atomic<int> x;
int i = x++;
和
extern std::atomic<int> x;
int i = x.fetch_add(1);
我觉得第二个版本更安全,但是在这两个版本之间的测试中我看不到任何差异。
区别绝对不是两种方法都保证的安全性=原子性。
我认为最重要的区别是fetch_add()
可以采用不同的内存顺序参数,而对于增量运算符,它始终为memory_order_seq_cst
。
[另一个明显的区别是fetch_add()
不仅可以将1
作为参数,而operator++
更可能使用lock inc
指令实现(尽管从理论上讲,没有什么可以阻止编译器执行此类操作) fetch_add(1)
的最优化)
因此回答您的确切问题,x++
和x.fetch_add(1)
在语义上没有任何重要区别。 doc says:
此函数的行为就像使用1和memory_order_seq_cst作为参数调用了atomic :: fetch_add。
[x.fetch_add(1)
和x++
完全相同
如果您相信cppreference,https://en.cppreference.com/w/cpp/atomic/atomic/operator_arith说:
T operator++() volatile noexcept; (1) T* operator++() volatile noexcept; (2)
1)执行原子预递增。等效于
fetch_add(1)+1
。2)执行原子后递增。等效于
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;
所以我们看到std::memory_order
的operator++
默认为std::memory_order_seq_cst
,这是可用的更强的一个,也请参见:https://en.cppreference.com/w/cpp/atomic/memory_order
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阐明C
和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); }
[我不明白为什么后缀会调用fetch_add
帮助程序,而前缀直接使用内置函数,但是最后它们全都归结为完成实际工作的GCC内部函数__atomic_fetch_add
和__atomic_add_fetch
。