为什么 std::fetch_add 返回旧值?

问题描述 投票:0回答:1

是什么设计目的或技术限制使得

std::fetch_add
的返回值是改变前的值?

c++ c++11 atomic language-design stdatomic
1个回答
4
投票

这两种方式都没什么大不了的,你可以模仿另一种方式。例如如果需要,

val.add_fetch(1)
可以用
1 + val.fetch_add(1)
实现。但是,GNU C
__atomic
内置函数提供了这两种
.

ISO C/C++ 只提供

fetch_add
而不是
add_fetch
的可能原因:在某些情况下,它使得在 x86 上实现更便宜;
lock xadd [mem], reg
留下 reg = mem 的旧值,mem = sum。提供原语而不是其他原语会鼓励人们围绕该构建块设计算法,也许可以避免需要额外的
add
指令。

大多数具有 LL/SC 原子的 RISC ISA 都有 3 个操作数指令,因此它们可以

add dst, src1, src2
并在代码稍后需要时将内存中的值原封不动地保留在另一个寄存器中。 (LL/SC
fetch_add(x)
通常会实现为
load-linked reg1, [mem]
/
add reg2, reg1, x
/
store-conditional reg3, reg2, [mem]
。使用基于 reg3 中成功/失败结果的重试循环。如果
fetch_add
返回值未使用,则
 add
可以覆盖
reg1
而不是使用新的 reg.

所以在大多数 RISC 上,无论哪种方式都很好,x86 是更相关的 ISA 之一,需要关心效率。


对于某些用例,

fetch_add
也是您想要的。例如对于在基于数组的循环缓冲区无锁队列中抓取桶的线程,以
std::atomic<unsigned> write_idx;
零初始化开始,您希望
.fetch_add
0
.

开始
  static std::atomic<unsigned> write_idx = 0;  // shared var

  // in each thread:
  unsigned my_buf = write_idx.fetch_add(1) & ((1<<size) - 1);

您将获得以

0
而不是
1
开头的值。对于许多用例来说,这似乎是一种合理的模式。

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