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。
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]
参考资料: