C++中简单类型bool变量的原子性
这个问题实际上跟平台有很大的相关性。我们可以从汇编来看。
linux(x86)平台:
int main() { bool f = true; if (f) { f = false; } return 0; }
(gdb) list 1 int main() 2 { 3 bool f = true; 4 if (f) 5 { 6 f = false; 7 } 8 return 0; 9 } (gdb) b 4 Breakpoint 1 at 0x602: file ../src/Test.cpp, line 4. (gdb) r Starting program: /home/ubuntu/workspace/Test/Debug/Test Breakpoint 1, main () at ../src/Test.cpp:4 4 if (f) (gdb) disassemble Dump of assembler code for function main(): 0x00005555555545fa <+0>: push %rbp 0x00005555555545fb <+1>: mov %rsp,%rbp 0x00005555555545fe <+4>: movb $0x1,-0x1(%rbp) => 0x0000555555554602 <+8>: cmpb $0x0,-0x1(%rbp) 0x0000555555554606 <+12>: je 0x55555555460c <main()+18> 0x0000555555554608 <+14>: movb $0x0,-0x1(%rbp) 0x000055555555460c <+18>: mov $0x0,%eax 0x0000555555554611 <+23>: pop %rbp 0x0000555555554612 <+24>: retq End of assembler dump.
从以上代码可以看到,对bool型的赋值操作 movb $0x1,-0x1(%rbp) 和对bool型的比较操作 cmpb $0x0,-0x1(%rbp) 都是一条汇编指令,可以认为是原子操作。
linux(arm)平台:
1 int main() 2 { 3 bool f = true; 4 if (f) 5 { 6 f = false; 7 } 8 return 0; 9 }
(gdb) list 1 2 3 int main() 4 { 5 bool f = true; 6 if (f) 7 { 8 f = false; 9 } 10 return 0; (gdb) b 6 Breakpoint 1 at 0x4005ac: file test.cpp, line 6. (gdb) r Starting program: /home/ubuntu/workspace/test/test Breakpoint 1, main () at test.cpp:6 6 if (f) (gdb) disassemble Dump of assembler code for function main(): 0x00000000004005a0 <+0>: sub sp, sp, #0x10 0x00000000004005a4 <+4>: mov w0, #0x1 // #1 0x00000000004005a8 <+8>: strb w0, [sp,#15] => 0x00000000004005ac <+12>: ldrb w0, [sp,#15] 0x00000000004005b0 <+16>: cmp w0, #0x0 0x00000000004005b4 <+20>: b.eq 0x4005bc <main()+28> 0x00000000004005b8 <+24>: strb wzr, [sp,#15] 0x00000000004005bc <+28>: mov w0, #0x0 // #0 0x00000000004005c0 <+32>: add sp, sp, #0x10 0x00000000004005c4 <+36>: ret End of assembler dump.
从以上代码可以看到,对bool型的赋值操作 strb w0, [sp,#15] ,读取操作 ldrb w0, [sp,#15] 和比较操作 cmp w0, #0x0 均是原子操作。但是,与x86不同的是,在arm上,读取与比较是分开的。所以多线程编程时,以bool型作为条件判断,可能会出来读取出值后,值又被改变了的情况,这可能会导致执行本以不该被执行的代码。