增量运算符的原子性
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)
原子增量操作。
我在一次采访中被告知,在 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)
原子增量操作。