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

时间:2014-08-15 18:41:42

标签: c++ multithreading c++11 parallel-processing atomic

之间有什么区别
extern std::atomic<int> x;
int i = x++;

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

我觉得第二个版本更安全,但我看不出这两个版本之间的测试存在任何差异。

2 个答案:

答案 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澄清了CA

  
      
  • 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/atomicatomic<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}这是真正的工作。

相关问题