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

pyt*_*nic 17 c c++ x86 gcc x86-64

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

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

Ira*_*ter 28

只有在使用LOCK前缀时,X86上的increment-memory机器指令才是原子的.

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

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

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

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


nos*_*nos 8

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

  1. 将值从内存加载到寄存器
  2. 递增寄存器的内容
  3. 将寄存器保存回内存.

这离原子还很远.

$ 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
Run Code Online (Sandbox Code Playgroud)

不要被被愚弄0x0mov指令,有足够的空间用于4个字节那里,链接器将在最终的存储器地址填写a在这个目标文件被链接在那里.


Mic*_*gan 8

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

线程1加载值0

线程2加载值0

线程1递增商店1

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

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


Jon*_*rdy 5

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