Meh*_*dad 6 c++ x86 atomic visual-c++ relaxed-atomics
在Visual C++ 2013上,当我编译以下代码时
#include <atomic>
int main()
{
std::atomic<int> v(2);
return v.fetch_add(1, std::memory_order_relaxed);
}
Run Code Online (Sandbox Code Playgroud)
我在x86上找回了以下程序集:
51 push ecx
B8 02 00 00 00 mov eax,2
8D 0C 24 lea ecx,[esp]
87 01 xchg eax,dword ptr [ecx]
B8 01 00 00 00 mov eax,1
F0 0F C1 01 lock xadd dword ptr [ecx],eax
59 pop ecx
C3 ret
Run Code Online (Sandbox Code Playgroud)
在x64上类似:
B8 02 00 00 00 mov eax,2
87 44 24 08 xchg eax,dword ptr [rsp+8]
B8 01 00 00 00 mov eax,1
F0 0F C1 44 24 08 lock xadd dword ptr [rsp+8],eax
C3 ret
Run Code Online (Sandbox Code Playgroud)
我根本就不明白:为什么变量的宽松增量int需要lock前缀?
这有什么原因,还是他们根本没有包括删除它的优化?
*我用/O2它/NoDefaultLib来修剪它并摆脱不必要的C运行时代码,但这与问题无关.
因为仍然需要锁才能成为原子锁;即使memory_order_relaxed对增量/减量的要求过于严格,也无法锁定。
想象一下没有锁的同一件事。
v = 0;
Run Code Online (Sandbox Code Playgroud)
然后我们产生100个线程,每个线程都带有以下命令:
v++;
Run Code Online (Sandbox Code Playgroud)
然后您等待所有线程完成,您期望v是什么?不幸的是,它可能不是100。假设一个线程加载了v = 23的值,并且在创建24之前,另一个线程也加载了23,然后也写出了24。因此,线程实际上彼此相反。这是因为增量本身不是原子的。当然,加载,存储,添加本身可能是原子的,但是递增是多个步骤,因此不是原子的。
但是使用std :: atomic时,无论std::memory_order设置如何,所有操作都是原子的。唯一的问题是它们将以什么顺序发生。memory_order_relaxed仍然保证原子性,就它附近发生的其他任何事情而言,即使按相同的值进行操作,也可能会乱序。