gcc原子内置函数

ddo*_*man 23 c gcc atomic

http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Atomic-Builtins.html

我相信以下代码会原子地增加var的值.

volatile int var = 0;
__sync_fetch_and_add( &var, 1 )
Run Code Online (Sandbox Code Playgroud)

我将上述代码理解为以下逻辑:

  1. 加载变量var的地址
  2. 将数字1写入变量var原子 - 通过寄存器/缓存,以某种方式

但是,我怀疑以下是否也是原子的

volatile int var = 0;
volatile int num = 1;
__sync_fetch_and_add( &var, num )
Run Code Online (Sandbox Code Playgroud)

因为它可能被解释为

  1. 加载变量var的地址
  2. 将变量num的值加载到寄存器中
  3. 将值写入变量var.

执行#2之后,但在#3之前,CPU /线程被中断,另一个CPU /线程更新变量num的值.

换句话说,当使用gcc的_ sync*()时,我可以使用变量而不是常量作为第二个参数吗?

它不会破坏原子性吗?

Die*_*Epp 30

该操作实际上是两个操作.

__sync_fetch_and_add( &var, num )
Run Code Online (Sandbox Code Playgroud)

加载num是原子的.添加到var原子.但是两个原子操作放在一起时不会进行原子操作.这就是为什么发明新的无锁数据结构如此困难的原因. 通常,两个线程安全操作在编写时不一定会进行线程安全操作. 这就是为什么制作正确的多线程应用程序如此困难的原因.

你看,__sync_fetch_and_add 它确实是原子的,但它的行为就像一个普通的函数 - 因此它将"num"的当前值作为参数.说函数的原子性被破坏是不正确的 - 因为调用者负责从中加载值num,并且它不是函数接口的一部分.我同样可以抱怨这个:

__sync_fetch_and_add(&var, some_really_long_function());
Run Code Online (Sandbox Code Playgroud)

或者更糟的是

__sync_fetch_and_add(long_function_1(), long_function_2());
Run Code Online (Sandbox Code Playgroud)

你说它"可能被解释为"

  1. 加载变量var的地址
  2. 加载变量num的值
  3. 执行原子添加

但是根据C规范,并不是它可以这样解释,而是必须以这种方式解释,否则编译器将不符合(实际上,它可以交换#1和#2,但这并不重要这里).

  • [自GCC 4.8起,不推荐使用__sync内置函数,而推荐使用__atomic内置函数](/sf/answers/2161493631/)只是将其留在这里供希望使用__sync的任何人使用 (2认同)