第一篇 TTD 專題 :C# 那些短命線程都在干什么?

一:背景1.講故事在分析的眾多dump中,經常會遇到各種奇葩的問題,僅通過dump這種快照形式還是有很多問題搞不定,而通過 perfview 這種粒度又太粗 , 很難找到問題之所在,真的很頭疼,比如本篇的 短命線程 問題,參考圖如下:

第一篇 TTD 專題 :C# 那些短命線程都在干什么?

文章插圖
我們在 t2 時刻抓取的dump對查看 短命線程 毫無幫助 , 我根本就不知道這個線程生前執行了什么代碼,為什么這么短命,還就因為這樣的短命讓 線程池 的線程暴增 。
為了能盡最大努力解決此類問題,武器庫中還得再充實一下,比如本系列要聊的 Time Travel Debug,即時間旅行調試 。
二: Time Travel Debug1. 什么是 時間旅行調試如果說 dump 是程序的一張照片,那 TTD 就是程序的一個短視頻,很顯然短視頻的信息量遠大于一張照片,因為視頻記錄著疑難雜癥的前因后果,參考價值巨大,簡直就是銀彈般的存在 。
三:案例演示1. 參考代碼這是我曾經遇到的一個真實案例,在沒有 TTD 的協助下最終也艱難的找到了問題 , 但如果有 TTD 的協助簡直就可以秒殺 , 為了方便說明,先上一個測試代碼 。
internal class Program{static void Main(string[] args){for (int i = 0; i < 200; i++){Task.Run(() =>{Test();});}Console.ReadLine();}public static int index = 1;static void Test(){Thread.Sleep(1000);var i = 10;var j = 20;var sum = i + j;Console.WriteLine($"i={index++},sum={sum}");}}程序跑完之后,我們抓一個dump文件,輸出如下 。
0:000> !tThreadCount:20UnstartedThread:0BackgroundThread: 7PendingThread:0DeadThread:13Hosted Runtime:noLock DBGIDOSID ThreadOBJState GC ModeGC Alloc ContextDomainCount Apt Exception0112f8 00C4AF2080030220 Preemptive03C3FFAC:03C40000 00c462f8 -00001 Ukn626a70 00C5BBD82b220 Preemptive03C521B8:03C53FE8 00c462f8 -00001 MTA (Finalizer)XXXX40 00C9FEB01039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)756694 00CA0990302b220 Preemptive03C40314:03C41FE8 00c462f8 -00001 MTA (Threadpool Worker)XXXX60 00CB53B81039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX70 00CB59581039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX80 00CB43381039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX90 00CB4C581039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX100 088792781039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)8115d10 08879E90102b220 Preemptive03C2AC2C:03C2BFE8 00c462f8 -00001 MTA (Threadpool Worker)XXXX120 0887D1F81039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX130 0887C0D81039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX140 0887AB701039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX150 0887B4001039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX160 0887D6401039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)XXXX170 0887A7281039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)9185658 0887C520102b220 Preemptive03C46684:03C47FE8 00c462f8 -00001 MTA (Threadpool Worker)1019564 0887C968102b220 Preemptive03C4A664:03C4BFE8 00c462f8 -00001 MTA (Threadpool Worker)XXXX200 0887AFB81039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)113547c 0887A2E02b220 Preemptive03C50008:03C51FE8 00c462f8 -00001 MTA 2. 為什么會有很多短命線程從 windbg 的輸出看有很多的 XXX,那原因是什么呢? 還得先觀察下代碼,可以看到代碼會給 ThreadPool 分發 100 次任務,每個任務也就 1s 的運行時間,這樣的代碼會造成 ThreadPool 的工作線程處理不及繼而會產生更多的工作線程,在某一時刻那些 Sleep 后的線程又會規模性喚醒,ThreadPool 為了能夠平衡工作者線程,就會滅掉很多的線程,造成 ThreadPool 中的暴漲暴跌現象 。
因果關系是搞清楚了,但對于落地是沒有任何幫助的,比如線程列表倒數第二行已死掉的線程:
XXXX200 0887AFB81039820 Preemptive00000000:00000000 00c462f8 -00001 Ukn (Threadpool Worker)你是沒法讓它起死回生的,對吧?這時候就必須借助 TTD 錄制一個小視頻 。
3. TTD 錄制錄制非常簡單,選擇 Lauch executable (advanced) 項再勾選 Record 即可,截圖如下:
第一篇 TTD 專題 :C# 那些短命線程都在干什么?

文章插圖
【第一篇 TTD 專題 :C# 那些短命線程都在干什么?】等程序執行完了或者你覺得時機合適再點擊 Stop and Debug 停止錄制,截圖如下:

推薦閱讀