是+ =,| =,& =等原子?

时间:2010-03-18 10:02:21

标签: c++ c operators thread-safety atomic

+=|=&=等“修改”运算符是否原子?

我知道++是原子的(如果你同时在两个不同的线程中执行x++;,那么你总是会x增加2,而不是{{1}优化已关闭。)

我想知道的是x=x+1和喜欢的内容是否是线程安全的,还是我必须使用互斥锁来保护它们?

(...还是依赖于CPU?在这种情况下,它在ARM上是怎么回事?)

12 个答案:

答案 0 :(得分:74)

你错了。不能保证++是原子的,也不能保证复合赋值运算符,或者对于任何C ++操作都没有。

答案 1 :(得分:9)

x++通常在3条指令中实现:将X读入寄存器,递增,然后将其写回内存。

您的帖子可能会被抢先一步。

答案 2 :(得分:6)

如果值的变化在核心之间可见,则+ =(例如)必须加载值,添加增量然后存储它。这意味着操作不会是原子的

为确保原子性,您需要在操作周围进行适当的锁定。

答案 3 :(得分:3)

不,他们不是原子的! 如果你需要对原始类型进行原子操作,并且你正在使用linux,你可以看看这里:http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html和/或atomic.h ...

答案 4 :(得分:2)

++可能在您的编译器/平台上是原子的,但在c ++规范中,它没有被定义为原子。

如果要确保以原子方式修改值,则应使用适当的方法,例如Windows上的Interlocked *。

所有其他例程都相同。如果你想要原子操作,你应该使用适当的调用,而不是标准调用。

答案 5 :(得分:2)

保证C或C ++中的

运算符是原子的。它们可能在您的平台上,但您不会确定。通常,唯一的原子操作是一个指令Test and Set,它通常在大多数现代CPU中以某种形式提供,作为实现信号量的基础。

答案 6 :(得分:2)

它依赖于编译器和CPU。一些指令集为这些指令集提供原子指令(在机器大小的整数上)。

但是,无法保证编译器将使用这些指令,并且不会以非线程安全的方式优化代码。您需要在汇编中编写例程或使用提供原子性的编译器特定技术(例如instrinsics)(或使用使用这些技术之一的库)。


特别是在ARM上: ORR / ADD / AND指令采用两个操作数并将结果放在寄存器中。两个操作数都可以与结果寄存器相同,因此它们可以用作原子|,+ =,& =。

当然,结果放在一个寄存器中,第一个操作数也必须来自一个寄存器,所以你必须确保寄存器加载是以原子方式完成的。

答案 7 :(得分:1)

它们不仅像所有操作一样不是原子的,而且它们可以有非常有趣的结果。例如,如果编译器发现它写入x,则允许使用x作为临时变量,而不是使用寄存器或堆栈空间。这意味着x可能暂时包含任何值,而不仅仅是对x

有意义的值

http://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong

答案 8 :(得分:1)

重复指向此处,这需要更新。 “新”C11语言启用了一个原子属性,承认:

_Atomic int a;
...
a += 3

可以编译成(原子)无界循环。感谢礼品标准人员,我真的希望你没有。

1:在某些体系结构中,原子操作只能在支持某些访问协议的内存上实现。例如,ARMv7,MIPS将序列转换为:

do {
    x = LoadLinked(a) + 3;
} while !StoreConditional(x, &a);

但对于某些内存/缓存类型,未定义LoadLinked / StoreConditional。喜欢调试。

2:相关的是 false sharing ,它是LoadLinked的工件,StoreConditional在缓存行(例如,32,64,256字节)上运行,而不是子块。所以:     _Atomic int a [4]; 可能需要4 *高速缓存行大小(因此1024字节)来安全地允许在[n]和[n + 1]上同时进行原子操作,因为4个cpu可能正在为了更新[0..3],但从未成功。

希望下一个标准能够识别属性修饰的固有失败,并将c89恢复为合法的C标准。

答案 9 :(得分:0)

值得一提的是,这些运算符可能会被重载,因此当然不能保证它们对所有类都是原子的。

答案 10 :(得分:0)

即使++是一个原子操作,也不意味着执行++x的两个线程会导致x正好高两个。你必须以某种方式同步线程,否则他们不一定会看到彼此的变化。

答案 11 :(得分:0)

您必须使用互斥锁来保护您的变量