C++ 更新双重操作是原子的吗

C++ 更新双重操作是原子的吗,c++,visual-c++,C++,Visual C++,在Java中,更新double和long变量可能不是原子的,因为double/long被视为两个独立的32位变量 在C++中,如果我使用32位英特尔处理器+微软Visual C++编译器,更新双(8字节)运算原子?p> 我找不到太多关于这种行为的说明 当我说“原子变量”时,我的意思是: 线程A试图将1写入变量x。 线程B试图将2写入变量x 我们将从变量x中得到值1或2,但不是一个未定义的值。我认为在任何体系结构中,线程/上下文切换都不会中途中断寄存器的更新,这样您就可以得到它将要更新的32位中

在Java中,更新double和long变量可能不是原子的,因为double/long被视为两个独立的32位变量

在C++中,如果我使用32位英特尔处理器+微软Visual C++编译器,更新双(8字节)运算原子?p> 我找不到太多关于这种行为的说明

当我说“原子变量”时,我的意思是:

线程A试图将1写入变量x。 线程B试图将2写入变量x


我们将从变量x中得到值1或2,但不是一个未定义的值。

我认为在任何体系结构中,线程/上下文切换都不会中途中断寄存器的更新,这样您就可以得到它将要更新的32位中的18位更新。更新内存位置也一样(前提是它是一个基本访问单元,8、16、32、64位等)。

这是特定于硬件的,取决于体系结构。对于x86和x86_64,如果8字节写入或读取对齐,则保证它们是原子的。引用英特尔体系结构内存订购白皮书:

英特尔64内存订购保证 对于以下每种情况 内存访问指令 组成内存操作出现 作为单个内存访问执行 无论内存类型如何:

  • 读取或写入单个字节的指令

  • 读取或写入地址为的字(2字节)的指令 在2字节边界上对齐

  • 读写地址为的双字(4字节)的指令 在4字节边界上对齐

  • 读取或写入地址为的四字(8字节)的指令 在8字节边界上对齐
  • 所有锁定的指令(隐式 锁定的xchg指令和其他 使用 锁前缀)是一个不可分割且 不间断负载序列 其次是商店,无论 内存类型和对齐方式


    那么这个问题得到了回答吗?我运行了一个简单的测试程序,更改了双精度:

    #include <stdio.h> int main(int argc, char** argv) { double i = 3.14159265358979323; i += 84626.433; } #包括 int main(int argc,字符**argv) { 双i=3.14159265358979323; i+=84626.433; } 我编译它时没有进行优化(gcc-O0),所有赋值操作都是用单个汇编指令执行的,比如
    fldl.LC0
    faddp%st,%st(1)
    。(
    i+=84626.433
    当然是由两个操作完成的,
    faddp
    fstpl


    线程真的会在单个指令(如
    faddp
    )中被中断吗?

    可以安全地假设,更新double永远不是原子的,即使它的大小与具有原子保证的int相同。原因是if具有不同的处理路径,因为它是一种非关键且昂贵的数据类型。例如,即使是数据屏障通常也会提到它们通常不适用于浮点数据/操作

    VisualC++将对齐原始类型(参见),而这应该保证它的比特不会在写入内存时被篡改(8字节的一致总是在一个64位或128位高速缓存行中),其余取决于CPU如何处理高速缓存中的非原子数据以及是否读取/刷新高速缓存行是可中断的。因此,如果您仔细阅读英特尔文档,了解您正在使用的内核类型,并且它为您提供了这一保证,那么您就可以安全使用

    Java规范之所以如此保守,是因为它应该在旧的386和Corei7上以相同的方式运行。这当然是妄想,但承诺就是承诺,因此承诺更少:-)

    我之所以说您必须查找CPU文档,是因为您的CPU可能是旧的386,或者类似的:-))不要忘记,在32位CPU上,您的8字节块需要2个“轮”才能访问,因此您只能依靠缓存访问机制的摆布


    提供更高数据一致性保证的缓存线刷新仅适用于具有Intel ian保证(自动缓存一致性)的合理最新CPU。

    在多核上,除了原子性之外,还必须担心缓存一致性,因此,当写入程序更新时,线程读取在其缓存中看到新值。

    问题不是上下文切换,而是多核和多cpu。即使在多核/多cpu体系结构中,物理内存访问也必须由内存控制器串行化。让多个设备同时访问同一电路在电气上是不可能的。内存控制器以块和数据总线宽度的整个单位访问内存,因此不可能对内存位置进行部分更新。那么,如果双缓存线位于两条缓存线的边界上,该怎么办?我怀疑MSVC++是否能做到这一点,因为所有东西都会以2的幂来调整大小。但是如果你在推广,这不是C++标准的要求(至少在ARM ABIS中的一个,长和双倍只需要4个对齐,而不是8个对齐)。它还依赖于编译器,这并不需要首先确保双打是8对齐的,或者使用一个四字OP来读或写。虽然你认为它可能会,而且我也希望VisualC++文档不管它是否有。是的,这是在编译器ABI中指定的。对于非自动变量,双精度始终对齐,除非明确指定为非对齐。对于32位系统中堆栈上的变量,如果堆栈以某种方式变得不对齐,它们可能会变得不对齐,例如,从外部非C程序调用函数。但无论如何,您都不想从函数中的堆栈返回对象……您不想自动返回对象,但您可以将指向它们的指针传递到您调用的函数中。但我想你所说的对提问者来说已经足够了——只要他能控制他们是如何定义的,他就能确保他的替身是原子的