是C ++读写int原子吗?

时间:2020-03-05 18:50:46  来源:igfitidea点击:

我有两个线程,一个线程更新一个int,另一个读取它。这是一个统计值,其中读取和写入的顺序无关紧要。

我的问题是,是否仍然需要同步对此多字节值的访问?或者换一种说法,可以完成部分写入并使其中断,然后进行读取。

例如,考虑一个值为0x0000FFFF的值,该值的增量值为0x00010000。

是否有一段时间我应该担心该值看起来像0x0001FFFF?当然,类型越大,越可能发生这种情况。

我一直同步这些类型的访问,但是很好奇社区的想法。

解决方案

回答

不,它们不是(或者至少我们不能认为它们是)。话虽这么说,但是有一些技巧可以自动完成,但是它们通常是不可移植的(请参阅"比较并交换")。

回答

是的,我们需要同步访问。在C ++ 0x中,它将是一场数据竞赛,并且是未定义的行为。使用POSIX线程,它已经是不确定的行为。

实际上,如果数据类型大于本机字大小,则可能会得到错误的值。同样,由于优化会移动读取和/或者写入,另一个线程可能永远也看不到写入的值。

回答

我们必须同步,但是在某些体系结构上,有进行同步的有效方法。

最好是使用子例程(可能在宏后面隐藏),以便我们可以有条件地将实现替换为特定于平台的实现。

Linux内核已经有一些这样的代码。

回答

如果我们正在读取/写入4字节的值,并且它在内存中是DWORD对齐的,并且我们正在I32体系结构上运行,则读写是原子的。

回答

男孩,什么问题。答案是:

Yes, no, hmmm, well, it depends

一切都取决于系统的体系结构。在IA32上,正确对齐的地址将是原子操作。未对齐的写入可能是原子的,这取决于所使用的缓存系统。如果内存位于单个L1缓存行内,则它是原子的,否则就不是原子的。 CPU和RAM之间的总线宽度可能会影响原子性质:在8086上正确对齐的16位写入是原子的,而在8088上进行相同的写入则不是,因为8088仅具有8位总线,而8086具有16位总线。

另外,如果我们使用的是C / C ++,请不要忘记将共享值标记为volatile,否则优化程序将认为该变量永远不会在线程之一中更新。

回答

我同意很多人,尤其是杰森。在Windows上,很可能会使用InterlockedAdd及其朋友。

回答

最初,人们可能会认为对本机计算机大小的读取和写入是原子的,但是要处理许多问题,包括处理器/内核之间的缓存一致性。在Windows上使用原子操作,例如Interlocked *,在Linux上使用等效操作。 C ++ 0x将具有一个"原子"模板,以将它们包装在一个不错的跨平台界面中。现在,如果我们正在使用平台抽象层,它可能会提供这些功能。 ACE可以,请参见类模板ACE_Atomic_Op。

回答

为了回应大家在楼上所说的话,C ++ 0x之前的语言无法保证有关从多个线程访问共享内存的任何信息。任何保证都取决于编译器。

回答

除了上面提到的缓存问题...

如果将代码移植到具有较小寄存器大小的处理器,它将不再是原子的。

IMO,线程问题太棘手,无法冒险。

回答

在Windows上,Interlocked *** Exchange *** Add被保证是原子的。

回答

唯一可移植的方法是为编译器使用signal.h标头中定义的sig_atomic_t类型。在大多数C和C ++实现中,这是一个整数。然后将变量声明为" volatile sig_atomic_t"。

回答

让我们举个例子

int x;
x++;
x=x+5;

假定第一条语句是原子的,因为它会转换为占用一个CPU周期的单个INC汇编指令。但是,第二个赋值需要几个操作,因此显然不是原子操作。

另一个例如

x=5;

同样,我们必须分解代码以查看此处实际发生的情况。

回答

tc,
我认为,当我们使用常量(如6)时,指令将不会在一个机器周期内完成。
尝试查看与x ++相比x + = 6的指令集