增量运算符的原子性

Atomicity of the Increment Operator

我在一次采访中被告知,在 C 中,使用 ++ 运算符(比如 i++)是一个原子操作,而 "i += 1" 则不是。我认为这些操作在线程安全或原子性方面完全相同。我是不是遗漏了什么,或者这些实际上是不同的?

胡说八道。根据数据类型、体系结构和编译器的不同,其中一个可能是原子性的,也可能不是原子性的(除非您使用的是 C11 原子,否则标准一般不会保证原子性),但我看不到任何有充分的理由认为,一般来说,i++ 是原子的,而 i += 1 不是。在未使用表达式结果的上下文中,它们很可能实际上生成了相同的代码。

语句* i++;i += 1; 在 C 中是等效的。两者都不能保证是原子的。

特别是,大于系统字长的变量增量(例如,32 位系统上的 64 位变量)几乎总是非原子的,因为以原子方式递增此类变量通常需要显式锁定。此外,某些体系结构不支持直接递增内存中的变量。 (也就是说,它们需要一个明确的 load/modify/store 序列。)这些架构不能在没有锁定的情况下自动修改 any 变量。

* 当被视为独立语句时,而不是表达式

面试官说错了

我用gcc-4.8.2编译了两个函数-O2-S

void increment1(int *a) {
  *a += 1;
}
void increment2(int *a) {
  (*a)++;
}

两者都完全生成 the same 程序集

increment1:
.LFB0:
        .cfi_startproc
        addl    , (%rdi)
        ret
        .cfi_endproc
.LFE0:
        .size   increment1, .-increment1
        .p2align 4,,15
        .globl  increment2
        .type   increment2, @function
increment2:
.LFB1:
        .cfi_startproc
        addl    , (%rdi)
        ret
        .cfi_endproc

但在更精确的技术术语中,它们都是atomic write,这意味着它不提供MAD结果。 如果在 32 位或更少位 CPU 环境中使用 int64_t 变量,64 位修改会导致多次写入操作。不能atomic write没有锁。

您可以在gcc环境下使用__sync_fetch_and_add(&a, 1)原子增量操作。