我已经陷入了一个混乱的混乱的多线程编程,并希望有人可以来和我的一些理解.
在做了相当多的阅读之后,我已经明白,我应该能够在64位系统1上设置64位int原子值的值.
我发现很多阅读困难,所以以为我会尝试做一个测试来验证这一点.所以我用一个线程写了一个简单的程序,它将一个变量设置为两个值之一:
bool switcher = false; while(true) { if (switcher) foo = a; else foo = b; switcher = !switcher; }
另一个线程会检查foo的值:
while (true) { __uint64_t blah = foo; if ((blah != a) && (blah != b)) { cout << "Not atomic! " << blah << endl; } }
我设一个= 1844674407370955161;和b = 1144644202170355111 ;.我运行这个程序,没有输出警告我,blah不是a或b.
好的,看起来可能是一个原子写…但是,我改变了第一个线程直接设置a和b,像这样:
bool switcher = false; while(true) { if (switcher) foo = 1844674407370955161; else foo = 1144644202170355111; switcher = !switcher; }
我重新跑了,突然间:
Not atomic! 1144644203261303193 Not atomic! 1844674406280007079 Not atomic! 1144644203261303193 Not atomic! 1844674406280007079
有什么变化无论哪种方式,我分配大量的foo – 编译器是否处理不同的数字,或者我误解了一切?
谢谢!
1:Intel CPU documentation,section 8.1,Guaranteed Atomic Operations
解决方法
反汇编循环,我得到以下代码与gcc:
.globl _switcher _switcher: LFB2: pushq %rbp LCFI0: movq %rsp,%rbp LCFI1: movl $0,-4(%rbp) L2: cmpl $0,-4(%rbp) je L3 movq _foo@GOTPCREL(%rip),%rax movl $-1717986919,(%rax) movl $429496729,4(%rax) jmp L5 L3: movq _foo@GOTPCREL(%rip),%rax movl $1486032295,(%rax) movl $266508246,4(%rax) L5: cmpl $0,-4(%rbp) sete %al movzbl %al,%eax movl %eax,-4(%rbp) jmp L2 LFE2:
所以看起来,gcc会使用32位的具有32位立即数值的movl指令.有一个指令movq可以将64位寄存器移动到存储器(或存储器到64位寄存器),但是似乎无法设置将立即值移动到存储器地址,因此编译器被强制要么使用临时寄存器,然后将值移动到内存,或者用于movl.您可以尝试强制使用一个临时变量使用寄存器,但是这可能无法正常工作.
参考文献: