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 澄清了C
和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
做真正的工作.
【讨论】:
以上是关于std::atomic<int>:x.fetch_add(1) 和 x++ 之间的区别;的主要内容,如果未能解决你的问题,请参考以下文章
我可以制作一个线程安全的 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