递增运算符的原子性

6

在一次面试中,我被告知,在C语言中使用++运算符(例如i++)是一个原子操作,而使用“i += 1”不是。我认为在线程安全或原子性方面,这些操作完全相同。我是否遗漏了什么,还是它们实际上确实不同?


10
面试官错误了 - 两者都不能保证是原子性的。 - Paul R
4
如果一个潜在的未来老板告诉你那句话,我建议你寻找其他潜在就业机会。 - WhozCraig
3
编译器可能会对表达式++ii += 1实现类似(它们相同是因为表达式的结果是增加后的值)。这与i++不同,因为表达式的结果是增加之前的值。作为完整语句,这三个是等价的。但它们都不能保证是原子操作。 - Jonathan Leffler
1
参见C语言中,i += 1是原子操作吗?,虽然没有提到i++或者++i,但是它们有密切的关联,所以不算是重复问题。 - Jonathan Leffler
这是一个非常好的例子,说明 C++ 不等于 C。对你的问题给个加一 (但要确保对检查人给个减一。除非你需要这份工作 ;))。 - frasnian
3个回答

11

这是毫无意义的。具体要看数据类型、架构和编译器(标准通常不会做一般情况下的原子性保证,除非你使用 C11 原子操作)是否支持,一个操作可能是原子的也可能不是,但我认为一般情况下没有充分的理由证明 i++ 是原子的而 i += 1 不是。在表达式结果无用的上下文中,它们很可能实际生成相同的代码。


8

在C语言中,i++;i += 1;这两个语句是等价的。但是两者都不能保证原子性。

特别地,对于大于系统字长的变量(例如32位系统上的64位变量),增量几乎总是非原子的,因为要想原子地增加这样的变量通常需要显式锁定。此外,一些体系结构不支持直接在内存中递增变量。(也就是说,它们需要一个显式的加载/修改/存储序列。)这些架构无法在没有锁定的情况下原子修改任何变量。

* 当作为单独的语句时,而不是表达式


进一步的信息:++i 被定义为 i += 1 - M.M

2

面试官是错误的。

我使用gcc-4.8.2编译了两个函数,其中包括-O2-S参数。

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

两者生成的汇编代码完全相同

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

但是在更精确的技术术语上下文中,它们都是“原子写入”,这意味着它们不会提供MAD结果。 如果您在32位或更低位CPU环境中使用int64_t变量,则64位修改会导致多次写操作。没有锁,它不能成为“原子写入”。

您可以在gcc环境中使用“__sync_fetch_and_add(&a, 1)”操作进行原子递增。


2
仅仅因为这两个函数在使用这个版本的GCC生成了相同的x86汇编并不意味着它们总是相同。不同的编译器和架构可能会有所不同。(你在这里是正确的,但只是偶然相同。) - user149341

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接