std :: atomic :x.fetch_add(1)与x ++之间的差异;

问题描述 投票:8回答:2

之间有什么区别

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

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

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

c++ multithreading c++11 parallel-processing atomic
2个回答
6
投票

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

我认为最重要的区别是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。


2
投票

[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_orderoperator++默认为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阐明CA

  • 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); }

[我不明白为什么后缀会调用fetch_add帮助程序,而前缀直接使用内置函数,但是最后它们全都归结为完成实际工作的GCC内部函数__atomic_fetch_add__atomic_add_fetch

© www.soinside.com 2019 - 2024. All rights reserved.