vav*_*ava 13 assembly multithreading atomic race-condition
今天我遇到了这个问题:
你有一个代码
static int counter = 0;
void worker() {
for (int i = 1; i <= 10; i++)
counter++;
}
Run Code Online (Sandbox Code Playgroud)
如果worker从两个不同的线程调用,那么两个线程counter完成后会有什么值?
我知道它实际上可能是任何东西.但是我的内部胆量告诉我,这counter++很可能会被翻译成单个汇编指令,如果两个线程都在同一个核心上执行,那么counter将是20.
但是,如果这些线程在不同的内核或处理器上运行,那么它们的微代码中是否存在竞争条件呢?是否可以将一个汇编指令视为原子操作?
Nat*_*man 19
特别是对于x86,关于你的例子:counter++,有很多方法可以编译.最简单的例子是:
inc counter
Run Code Online (Sandbox Code Playgroud)
这转化为以下微操作:
counter到CPU上的隐藏寄存器counter这基本上与以下相同:
mov eax, counter
inc eax
mov counter, eax
Run Code Online (Sandbox Code Playgroud)
请注意,如果counter加载和商店之间的某些其他代理更新,它将不会counter在商店之后反映出来.此代理可以是同一内核中的另一个线程,同一CPU中的另一个内核,同一系统中的另一个CPU,甚至是使用DMA(直接内存访问)的某个外部代理.
如果您想保证这inc是原子的,请使用lock前缀:
lock inc counter
Run Code Online (Sandbox Code Playgroud)
lock保证没有人可以counter在加载和商店之间进行更新.
关于更复杂的指令,您通常不能假设它们将以原子方式执行,除非它们支持lock前缀.
并非总是如此 - 在某些体系结构中,一个汇编指令被转换为一个机器代码指令,而在另一些体系结构指
另外 - 你永远不能假设你正在使用的程序语言正在将一段看似简单的代码编译成一个汇编指令.而且,在某些体系结构中,您不能假设一个机器代码将以原子方式执行.
请使用适当的同步技术,具体取决于您编写的语言.
答案是:这取决于!
这里有一些混乱,汇编指令是什么.通常,一个汇编程序指令被转换为恰好一个机器指令.例外情况是你使用宏 - 但你应该知道这一点.
那说,问题归结为一个机器指令原子?
在过去的好时光中,它是.但今天,复杂的CPU,长时间运行的指令,超线程,......事实并非如此.某些CPU保证某些递增/递减指令是原子的.原因是,它们非常简单,可以进行非常简单的同步.
一些CPU命令也没有那么成问题.当你有一个简单的提取(处理器可以一件获取的一个数据) - 提取本身当然是原子的,因为没有任何东西可以分开.但是当你有未对齐的数据时,它会再次变得复杂.
答案是:这取决于.仔细阅读供应商的机器使用说明书.有疑问,它不是!
编辑:哦,我现在看到了,你也要求++计数器."最有可能被翻译"的陈述根本不可信.这在很大程度上还取决于编译器当然!当编译器进行不同的优化时,它会变得更加困难.