
正文
又一起.NET程序挂死, 用 Windbg 抽丝剥茧式的真实案例分析
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
一:背景1. 讲故事
前天有位粉丝朋友在后台留言让我帮忙看看他的 Winform程序 UI无响应 + 410线程 到底是啥情况,如下图:

说实话,能看到这些真实案例我是特别喜欢的 ,就像医生看病,光停留在理论和那些 demo 上,那是没有前途的,如果有朋友在这块搞不定的话,我可以免费帮你解读 dump,再附送一篇博客详述。
好了,言归正传,既然粉丝朋友已经提到了高达 410 线程,我本能反应就是要么高负载,要么野线程,后者大多是无数新出现的线程卡在某个锁上。
WinForm 出现高负载的情况,我至今还是没遇到,如果说卡在某个锁上,基本都属于这类,有了这个先入为主的思路,接下来就可以祭出 windbg 一探究竟了。
二: windbg 分析1. 查找 CLR 同步块表
十个人用锁,八个人会用 lock, 所以先用 !syncblk 看看程序的锁情况。
0:000> !syncblk
Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner
76 070e5fa4 67 1 17367570 15e8 218 03e6dd68 System.IO.Ports.SerialStream
-----------------------------
Total 789
CCW 39
RCW 2
ComClassFactory 1
Free 535
我去,从卦象上来看情况很不好,我来简单分析下。
- MonitorHeld = 67
这个 67 表示当前有 1 个线程持有锁,有 33 个线程在等待锁,肯定有朋友想问怎么算的?很简单:当一个线程持有了锁的时候 MonitorHeld+1 ,当一个线程在等待锁的时候 MonitorHeld+2 ,所以表达式就是: 67= [1 + 66=(33*2)]。
- Owning Thread Info = 17367570 15e8 218
上面三个信息都表示当前持有线程,可以看最后的 218,它是 windbg 映射出来的线程ID,如果不信的话,可以用 !t 来一探究竟。
0:000> !t
ThreadCount: 315
UnstartedThread: 0
BackgroundThread: 302
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 c64 00cc3de0 24220 Preemptive 042E1884:00000000 00cbc0a0 0 STA
214 240 1398 16702b90 1029220 Preemptive 00000000:00000000 00cbc0a0 0 MTA (Threadpool Worker)
215 323 b5c 12ab7260 1029220 Preemptive 00000000:00000000 00cbc0a0 0 MTA (Threadpool Worker)
216 290 1858 16c21c98 1029220 Preemptive 00000000:00000000 00cbc0a0 0 MTA (Threadpool Worker)
218 117 15e8 17367570 1029220 Preemptive 00000000:00000000 00cbc0a0 1 MTA (Threadpool Worker)
...
对,就是 218 这个罪魁祸首在持有了锁,导致 33 个线程在无辜的等待它。。。
- SyncBlock Owner = System.IO.Ports.SerialStream
也许你会好奇,到底 lock 持有的是哪一个对象呢?从 SyncBlock Owner 上看就是 SerialStream,








