Windbg如何排查程序死锁

1、关于程序死锁,网上也有很多博客。造成死锁的原因,基本上都是吃着碗里的,看着对方锅里面的。锁定资源互相等对方释放资源造成的。外网上找到了一个比较贴近实际生活应用的程序。

程序也很简单。

 1     public class Account
 2     {
 3         public int ID { get; }
 4         private double Balance;
 5         public Account(int id, double balance)
 6         {
 7             ID = id;
 8             Balance = balance;
 9         }
10 
11         public void WithdrawMoney(double amount)
12         {
13             Balance -= amount;
14         }
15         public void DepositMoney(double amount)
16         {
17             Balance += amount;
18         }
19     }
 1    public class AccountManager
 2     {
 3         private Account FromAccount;
 4         private Account ToAccount;
 5         private double TransferAmount;
 6 
 7         public AccountManager(Account AccountFrom, Account AccountTo, double AmountTransfer)
 8         {
 9             FromAccount = AccountFrom;
10             ToAccount = AccountTo;
11             TransferAmount = AmountTransfer;
12         }
13 
14         public void FundTransfer()
15         {
16             Console.WriteLine($"{Thread.CurrentThread.Name} trying to acquire lock on {FromAccount.ID}");
17             lock (FromAccount)
18             {
19                 Console.WriteLine($"{Thread.CurrentThread.Name} acquired lock on {FromAccount.ID}");
20                 Console.WriteLine($"{Thread.CurrentThread.Name} Doing Some work");
21                 Thread.Sleep(1000);
22                 Console.WriteLine($"{Thread.CurrentThread.Name} trying to acquire lock on {ToAccount.ID}");
23 
24                 lock (ToAccount)
25                 {
26                     FromAccount.WithdrawMoney(TransferAmount);
27                     ToAccount.DepositMoney(TransferAmount);
28                 }
29             }
30         }
31     }
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 
 7 namespace WindbugDemo
 8 {
 9     class DeadLock
10     {
11         public static void Main()
12         {
13             Console.WriteLine("Main Thread Started");
14             Account Account1001 = new Account(1001, 5000);
15             Account Account1002 = new Account(1002, 3000);
16             AccountManager accountManager1 = new AccountManager(Account1001, Account1002, 5000);
17             Thread thread1 = new Thread(accountManager1.FundTransfer)
18             {
19                 Name = "Thread1"
20             };
21             AccountManager accountManager2 = new AccountManager(Account1002, Account1001, 6000);
22             Thread thread2 = new Thread(accountManager2.FundTransfer)
23             {
24                 Name = "Thread2"
25             };
26             thread1.Start();
27             thread2.Start();
28             thread1.Join();
29             thread2.Join();
30             Console.WriteLine("Main Thread Completed");
31             Console.ReadKey();
32         }
33     }
34 }

2、程序运行后,见下图,然后通过微软网站下载的procdump工具生成dump。

Windbg如何排查程序死锁

Windbg如何排查程序死锁

3、用windbg打开dump文件后,执行 .loadby sos clr 以及 .load sosex.dll 命令。

4、分别切换到相应的线程,然后执行!dso命令,发现线程3和线程4在互相等待对方解锁。其实从程序的输出语句结合程序也可以判断出这个地方出现死锁。

0:000> !threads
ThreadCount:      4
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                                                        Lock  
       ID OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1 4ba0 00000000010a50d0  202a020 Preemptive  0000000003016858:0000000003017FD0 0000000001098b40 0     MTA 
   2    2 46e0 00000000010cfa70    2b220 Preemptive  0000000000000000:0000000000000000 0000000001098b40 0     MTA (Finalizer) 
   3    3 485c 00000000010f8790  202b020 Preemptive  0000000003018B50:0000000003019FD0 0000000001098b40 1     MTA 
   4    4 4bec 00000000010f9440  202b020 Preemptive  000000000301A4A8:000000000301BFD0 0000000001098b40 1     MTA 


0:000> ~3s
ntdll!NtWaitForMultipleObjects+0x14:
00007ffd`2ab7cbc4 c3              ret
0:003> !clrstack
OS Thread Id: 0x485c (3)
        Child SP               IP Call Site
000000001b9dedc8 00007ffd2ab7cbc4 [GCFrame: 000000001b9dedc8] 
000000001b9deee8 00007ffd2ab7cbc4 [GCFrame: 000000001b9deee8] 
000000001b9def48 00007ffd2ab7cbc4 [HelperMethodFrame_1OBJ: 000000001b9def48] System.Threading.Monitor.Enter(System.Object)
000000001b9df040 00007ffca1c80e4e WindbugDemo.AccountManager.FundTransfer() [D:\MyProjects\WindbugDemo\WindbugDemo\DeadLock\AccountManager.cs @ 32]
000000001b9df140 00007ffcffe4df12 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000001b9df210 00007ffcffe4dd95 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
000000001b9df240 00007ffcffe4dd65 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
000000001b9df290 00007ffcffef3e85 System.Threading.ThreadHelper.ThreadStart()
000000001b9df4e0 00007ffd011b6913 [GCFrame: 000000001b9df4e0] 
000000001b9df840 00007ffd011b6913 [DebuggerU2MCatchHandlerFrame: 000000001b9df840] 

0:003> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
3 00000000010fa3a8 3 1 00000000010f9440 4bec 4 00000000030164e8 WindbugDemo.Account
4 00000000010fa3f8 3 1 00000000010f8790 485c 3 00000000030164c8 WindbugDemo.Account
-----------------------------
Total 4
CCW 0
RCW 0
ComClassFactory 0
Free 0
0:003> !syncblk -all
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
1 00000000010fa308 0 0 0000000000000000 none 0000000003016570 System.Threading.Thread
2 00000000010fa358 0 0 0000000000000000 none 00000000030166e0 System.Threading.Thread
3 00000000010fa3a8 3 1 00000000010f9440 4bec 4 00000000030164e8 WindbugDemo.Account
4 00000000010fa3f8 3 1 00000000010f8790 485c 3 00000000030164c8 WindbugDemo.Account
-----------------------------
Total 4
CCW 0
RCW 0
ComClassFactory 0

0:003> !dso
OS Thread Id: 0x485c (3)
RSP/REG          Object           Name
000000001B9DEAA0 0000000003012eb0 Microsoft.Win32.SafeHandles.SafeFileHandle
000000001B9DEE00 0000000003016570 System.Threading.Thread
000000001B9DEE48 0000000003015708 System.Text.EncoderNLS
000000001B9DEE58 00000000030164e8 WindbugDemo.Account
000000001B9DEEC8 00000000030164e8 WindbugDemo.Account

...

000000001B9DEFE0 0000000003016380 System.IO.TextWriter+SyncTextWriter
000000001B9DEFF0 0000000003018a80 System.String    Thread1 trying to acquire lock on 1002
000000001B9DF028 0000000003016508 WindbugDemo.AccountManager

...

000000001B9DF6B0 0000000003016638 System.Threading.ThreadStart
0:003> ~4s
ntdll!NtWaitForMultipleObjects+0x14:
00007ffd`2ab7cbc4 c3              ret
0:004> !dso
OS Thread Id: 0x4bec (4)
RSP/REG          Object           Name
000000001BADEAF0 0000000003012eb0 Microsoft.Win32.SafeHandles.SafeFileHandle
000000001BADEE50 00000000030166e0 System.Threading.Thread
000000001BADEE98 0000000003015708 System.Text.EncoderNLS
000000001BADEEA8 00000000030164c8 WindbugDemo.Account
000000001BADEF18 00000000030164c8 WindbugDemo.Account

...

000000001BADF040 000000000301a3d8 System.String    Thread2 trying to acquire lock on 1001
000000001BADF078 0000000003016678 WindbugDemo.AccountManager
000000001BADF080 0000000003016740 System.Threading.ThreadHelper
000000001BADF090 00000000030165f8 System.Threading.ContextCallback
000000001BADF0A0 0000000003016678 WindbugDemo.AccountManager
000000001BADF0C0 000000000301a3d8 System.String    Thread2 trying to acquire lock on 1001
000000001BADF0C8 0000000003012d68 System.String    Thread2

...

000000001BADF700 0000000003016768 System.Threading.ThreadStart

5、当然,也可以用sosex扩展,更快捷的找出死锁的地方。

0:003> !dlk
Examining SyncBlocks...
Scanning for ReaderWriterLock instances...
Scanning for holders of ReaderWriterLock locks...
Scanning for ReaderWriterLockSlim instances...
Scanning for holders of ReaderWriterLockSlim locks...
Examining CriticalSections...
Could not find symbol ntdll!RtlCriticalSectionList.
Scanning for threads waiting on SyncBlocks...
Scanning for threads waiting on ReaderWriterLock locks...
Scanning for threads waiting on ReaderWriterLocksSlim locks...
Scanning for threads waiting on CriticalSections...
*DEADLOCK DETECTED*
CLR thread 0x3 holds the lock on SyncBlock 00000000010fa3f8 OBJ:00000000030164c8[WindbugDemo.Account]
...and is waiting for the lock on SyncBlock 00000000010fa3a8 OBJ:00000000030164e8[WindbugDemo.Account]
CLR thread 0x4 holds the lock on SyncBlock 00000000010fa3a8 OBJ:00000000030164e8[WindbugDemo.Account]
...and is waiting for the lock on SyncBlock 00000000010fa3f8 OBJ:00000000030164c8[WindbugDemo.Account]
CLR Thread 0x3 is waiting at WindbugDemo.AccountManager.FundTransfer()(+0xc8 IL,+0x24e Native) [D:\MyProjects\WindbugDemo\WindbugDemo\DeadLock\AccountManager.cs @ 32,17]
CLR Thread 0x4 is waiting at WindbugDemo.AccountManager.FundTransfer()(+0xc8 IL,+0x24e Native) [D:\MyProjects\WindbugDemo\WindbugDemo\DeadLock\AccountManager.cs @ 32,17]

 

参考资料:

https://dotnettutorials.net/lesson/deadlock-in-csharp/

Windbg如何排查程序死锁

上一篇:Windows-005-显示隐藏文件


下一篇:视觉十四讲:BA优化_g2o