正在更新双重操作原子

时间:2009-08-18 09:14:20

标签: c++ visual-c++

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

http://java.sun.com/docs/books/jls/second_edition/html/memory.doc.html#28733

在C ++中,如果我使用的是32位Intel处理器+ Microsoft Visual C ++编译器,那么更新双(8字节)操作原子?

我找不到有关此行为的详细说明。

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

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

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

5 个答案:

答案 0 :(得分:11)

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

  

Intel 64内存订购保证   对于以下每一个   内存访问指令,   组成记忆操作出现   作为单个内存访问执行   无论内存类型如何:

     
      
  1. 读取或写入单个字节的指令。

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

  4.   
  5. 读取或写入地址为的双字(4字节)的指令   在4字节边界上对齐。

  6.   
  7. 读取或写入地址为的四字(8字节)的指令   在8字节边界上对齐。
  8.         

    所有锁定的指令(隐含地   锁定xchg指令和其他   读取 - 修改 - 写入指令   锁定前缀)是不可分割的   不间断负载序列   然后是商店,无论如何   记忆类型和对齐方式。

答案 1 :(得分:2)

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

Visual C ++将对原始类型进行对齐(请参阅article),同时应该保证在写入内存时它的位不会出现乱码(8字节对齐始终在一个64位或128位高速缓存行中) rest取决于CPU如何处理其缓存中的非原子数据以及读取/刷新缓存行是否可中断。因此,如果您通过英特尔文档挖掘您正在使用的核心类型,它会为您提供保证,那么您就可以放心使用。

Java规范如此保守的原因在于它应该在旧版386和Corei7上以相同的方式运行。这当然是妄想,但承诺是一种承诺,因此它承诺较少:-)

我说你必须查找CPU文档的原因是你的CPU可能是旧的386,或类似的:-))不要忘记在32位CPU上你的8字节块需要2“轮”访问,因此您可以接受缓存访问机制的左右。

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

答案 2 :(得分:-2)

我不认为在任何体系结构中,线程/上下文切换会中断更新寄存器中途,以便您可以保留例如更新其将要更新的32位的18位。同样更新内存位置(假设它是基本访问单元,8,16,32,64位等)。

答案 3 :(得分:-2)

这个问题已经回答了吗?我运行了一个简单的测试程序来改变一个双:

#include <stdio.h>

int main(int argc, char** argv)
{
    double i = 3.14159265358979323;
    i += 84626.433;
}

我在没有优化的情况下编译它(gcc -O0),并且所有赋值操作都使用单个汇编程序指令执行,例如fldl .LC0faddp %st, %st(1)。 (i += 84626.433当然要完成两项操作,faddpfstpl)。

线程是否真的在单个指令中被中断,例如faddp

答案 4 :(得分:-2)

在多核上,除了原子之外,你还要担心缓存的一致性,这样当编写器更新时,线程读取会在缓存中看到新值。