为什么i ++在单核机器上不是线程安全的?

Dej*_*jas 4 assembly

可能重复:
增量器/减量器(var ++,var--)等线程是否安全?

您能否在汇编代码级别为我描述为什么在单个核心机器上将两个不同线程的值递增并不安全?

SLa*_*aks 9

i++ 有三个操作:

  • 获取i寄存器
  • 递增寄存器
  • 把它写回去 i

在这些操作之间,线程可能会被调度程序中断,以便可以运行(和修改i)不同的线程.


aro*_*oth 9

考虑可能为语句生成的指令i++.当然这取决于你的架构/指令集,但它可能会有以下几点:

LOAD    @i, r0    ;load the value of 'i' into a register from memory
ADD     r0, 1     ;increment the value in the register
STORE   r0, @i    ;write the updated value back to memory
Run Code Online (Sandbox Code Playgroud)

现在考虑如何在操作系统中实现多线程,无论机器有多少个核心.在最基本的层面上,操作系统需要一些工具来中断当前线程的执行,保存其状态,并执行上下文切换到不同的线程.操作系统没有自动方式知道用户线程内的哪些指令应被视为原子操作,并且能够在任意两条指令之间启动上下文切换.

如果OS执行从一个线程之间的其他上下文切换会发生什么LOADADD?假设i从值0开始,因此r0在第一个线程被换出时将设置为0.操作系统会将此值保存为该线程状态的一部分.现在第二个线程运行,并执行相同的LOAD语句.内存中的值仍为0,因此r0再次加载0.线程将值递增并将其写回内存,将值设置i为1.现在第一个线程恢复执行,操作系统将值恢复r0为0作为其上下文切换的一部分.第一个线程现在执行增量,设置r0为1,值1 i再次存储.现在值i不正确,因为已应用了两个增量,但该值仅增加了1.

简而言之,即使i++是高级语言中的单个语句,它也会生成多个汇编语言指令,并且操作系统/运行时环境不会将这些指令视为原子,除非您在周围添加额外的同步逻辑他们.

  • 在x86上,这实际上可以在单个指令中完成,例如"inc [eax]",其中eax是指向包含"i"的存储器的寄存器.这假定"i"甚至存储在存储器中(未优化到寄存器),并且编译器选择使用"inc"指令.因此,基本上即使在具有单指令存储器增量的架构上也不能依赖于此. (2认同)