在x86中增量是一个整数原子?

时间:2012-05-08 17:48:09

标签: c++ c gcc x86 x86-64

在多核x86机器上,假设在core1上执行的线程递增整数变量a,同时核心2上的线程也递增它。鉴于a的初始值为0,最终总是2?或者它可能有其他价值?假设a被声明为volatile并且我们没有使用原子变量(例如C ++的原子<>并且在gcc中内置原子操作)。

如果a的值在这种情况下确实总是2,这是否意味着x86-64中的long int也具有相同的属性,即a到最后总是2?

4 个答案:

答案 0 :(得分:27)

只有在使用 LOCK 前缀时,X86上的增量内存机器指令才是原子的。

C和C ++中的

x ++ 没有原子行为。如果您执行解锁增量,由于处理器正在读取和写入X的比赛,如果两个单独的处理器尝试增加,您最终只能看到一个增量或两者都被看到(第二个处理器可能已经读取了初始值,增加了它,并在第一次写回结果后将其写回。)

我相信C ++ 11提供了原子增量,并且大多数供应商编译器都有惯用的方法来导致某些内置整数类型(通常是int和long)的原子增量;请参阅编译器参考手册。

如果要增加“大值”(例如,多精度整数),则需要使用一些标准锁定机制(如信号量)。

请注意,您还需要担心原子读取。在x86上,如果是64位字对齐,则读取32位或64位值恰好是原子的。对于“大价值”而言,情况并非如此;再次,你需要一些标准锁。

答案 1 :(得分:8)

这是一个证明它在特定实现(gcc)中不是原子的, 如您所见(?),gcc生成

代码
  1. 将值从内存加载到寄存器
  2. 递增寄存器的内容
  3. 将寄存器保存回内存。
  4. 这远非原子。

    $ cat t.c
    volatile int a;
    
    void func(void)
    {
        a++;
    }
    [19:51:52 0 ~] $ gcc -O2 -c t.c
    [19:51:55 0 ~] $ objdump -d t.o
    
    t.o:     file format elf32-i386
    
    
    Disassembly of section .text:
    
    00000000 <func>:
       0:   a1 00 00 00 00          mov    0x0,%eax
       5:   83 c0 01                add    $0x1,%eax
       8:   a3 00 00 00 00          mov    %eax,0x0
       d:   c3                      ret
    

    不要被0x0指令中的mov所欺骗,那里有4个字节的空间,并且链接器将填充a的结果内存地址。目标文件已链接。

答案 2 :(得分:8)

由于没有人回答你的实际问题,而是以一种始终有效的方式向你展示如何做到这一点:

线程1加载值0

线程2加载值0

线程1递增商店1

线程2递增其本地寄存器值的副本并存储1。

正如您所看到的,最终结果是一个等于1但不是2的值。最后它不会总是2。

答案 3 :(得分:5)

不保证。您可以使用lock xadd指令来实现相同的效果,或者使用C ++ std::atomic,或者使用#pragma omp atomic,或者编写的任何其他并发解决方案来节省您的麻烦重新发明轮子。