看到一篇关于lock-free的文章 https://preshing.com/20120612/an-introduction-to-lock-free-programming/
里面有段这个代码,X的初始值为0,让读者思考,当两个线程运行这段代码时,如何使得两个线程都陷入死循环。
Here’s a simple example of an operation which contains no mutexes, but is still not lock-free. Initially, X = 0. As an exercise for the reader, consider how two threads could be scheduled in a way such that neither thread exits the loop.
while (X == 0)
{
X = 1 - X;
}
我反汇编下这段代码,这样就便于理解为何这段代码是线程不安全的。
00000000004008b6 <worker>:
4008b6: 55 push %rbp
4008b7: 48 89 e5 mov %rsp,%rbp
4008ba: 48 89 7d f8 mov %rdi,-0x8(%rbp)
4008be: eb 15 jmp 4008d5 <worker+0x1f>
4008c0: 8b 05 c6 07 20 00 mov 0x2007c6(%rip),%eax # 60108c <x>
4008c6: ba 01 00 00 00 mov $0x1,%edx
4008cb: 29 c2 sub %eax,%edx
4008cd: 89 d0 mov %edx,%eax
4008cf: 89 05 b7 07 20 00 mov %eax,0x2007b7(%rip) # 60108c <x>
4008d5: 8b 05 b1 07 20 00 mov 0x2007b1(%rip),%eax # 60108c <x>
4008db: 85 c0 test %eax,%eax
4008dd: 74 e1 je 4008c0 <worker+0xa>
4008df: b8 00 00 00 00 mov $0x0,%eax
4008e4: 5d pop %rbp
4008e5: c3 retq
因为初始值为0,即 0x2007c6(%rip)的内存处值为0
- 假设线程A和线程B都运行到路径:
4008be -->4008d5-->4008db-->4008dd-->4008c0
那么当前状态为0x2007b7(%rip)内存值为0,线程A和线程B的%eax寄存器的内容为0 - 线程A继续运行,此刻线程B并没有运行:
4008c0-->4008c6-->4008cb-->4008cd-->4008cd-->4008cf
当前状态为0x2007b7(%rip)内存值为1,线程A的%eax寄存器内容为1,线程B的%eax的内容为0. - 线程B继续运行,此刻线程A并没有运行:
4008c0-->4008c6-->4008cb-->4008cd-->4008cd-->4008cf
当前状态为0x2007b7(%rip)内存值为0,线程A的%eax寄存器内容为1,线程B的%eax的内容为0. - 线程A运行4008d5
当前状态为0x2007b7(%rip)内存值为0,线程A的%eax寄存器内容为0,线程B的%eax的内容为0. - 最后线程A和线程B陆续运行4008db,两个判断%eax为0,设置条件码ZF,然后继续循环下去了。
要理解这里,学会利用读懂汇编代码很有用处的。
这段c代码用gcc O1优化后,汇编如下
4008e6: 83 3d 9f 07 20 00 00 cmpl $0x0,0x20079f(%rip) # 60108c <x>
4008ed: 75 0a jne 4008f9 <worker+0x13>
4008ef: c7 05 93 07 20 00 01 movl $0x1,0x200793(%rip) # 60108c <x>
4008f6: 00 00 00
4008f9: b8 00 00 00 00 mov $0x0,%eax
4008fe: c3 retq
网友评论