上一篇博文給你們分享了使用Windbg分析內存泄露問題:html
本篇咱們繼續跟你們分享,如何分析解決線程阻塞問題。優化
從根本上講,線程阻塞屬於程序Hang的一種,其表現主要有:spa
1. 隨着請求的增長,線程數一直增長,可能會把線程池打爆操作系統
2. 低CPU使用率(被阻塞後的CPU使用率下降)線程
3. 請求沒有返回,客戶端一直在等待,直至Timeout。3d
那麼,從線程狀態上看,什麼是阻塞? 一個線程經歷的5個狀態,建立,就緒,運行,阻塞,終止。各個狀態的轉換條件以下圖:調試
上圖中有個阻塞狀態,就是說當線程中調用某個函數,須要IO請求,或者暫時得不到競爭資源的,操做系統會把該線程阻塞起來,避免浪費CPU資源,等到獲得了資源,再變成就緒狀態,等待CPU調度運行。code
線程發生阻塞的現象就是,進程的線程數會愈來愈多!htm
線程阻塞問題的分析思路:
持續請求過程當中,抓兩個或者三個Dump,看線程增長的速度,每次抓Dump間隔30s或者1分鐘
對比的看每一個Dump中:
1. 查看線程池的大小:!threadpool,有時間間隔兩個Dump對比着看,看線程池的線程數的增加狀況:
2. 查看線程的分類和線程的狀態 !threads,從下圖能夠看出,後臺線程一直在增長
3. 查看線程阻塞 !syncblk,也是看這兩個dump,對比着看
咱們發現:
第一個Dump中95號線程 阻塞了(1021-1)/2=510個線程
第二個Dump中79號線程 阻塞了(1099-1)/2=549個線程
95號線程獨佔的對象資源 00000026ba7c4dc0 (System.Object)
79號線程獨佔的對象資源也是00000026ba7c4dc0(System.Object)
兩個線程同時鎖住了同一個資源!00000026ba7c4dc0(System.Object)
此時,線程阻塞問題已經肯定,接下來,咱們要重點分析阻塞的根源線程(持有什麼資源不釋放,致使其餘線程在等待),95號線程、79號線程
4. 查看阻塞線程的根源線程、線程請求的資源對象 、被阻塞的線程數
例如 95號線程:
1 查看阻塞根源線程的堆棧 2 ~95s 3 !clrstack
經過線程堆棧,咱們在棧頂發現,95號線程,在等待Socket Server返回,具體再等哪一個Socket Server?
經過如下命令找出當前線程堆棧上的Socket對象
!dso
這樣咱們就定位出95號線程在作什麼,在等待什麼:
95號線程在等待SocketServer 10.*.*.*:80返回數據,獨佔資源:00000026ba7c4dc0(System.Object)
同時咱們經過線程堆棧看到了咱們本身的一個TCP通信類TCPInvoker, 在建立一個新的TCP鏈接,TCPInvoker類的代碼須要重點關注,繼續看!
咱們繼續看79號線程:
1 ~79s 2 !clrstack
經過79號線程的堆棧和阻塞狀況能夠發現:
79號線程Monitor.Enter(object),在請求資源的獨佔鎖:00000026ba7c4dc0(System.Object)
TCPInvoker卡在了GetInvoker方法上。咱們須要看看TCPInvoker的代碼了
5. 分析線程阻塞的緣由,改進代碼
從以下的代碼中,咱們能看到:
95號線程是執行到了GetInvoker方法的Create,Create中在等待Socket Server返回,此時若是Socket Server沒有響應,超時時間默認是5s,會一直持有資源00000026ba7c4dc0(System.Object)不釋放
79號線程也執行到了GetInvoker方法,可是在Lock時,等待95號線程釋放資源:00000026ba7c4dc0(System.Object)
隨着請求愈來愈多,超時+重試鏈接Socket Server,致使線程阻塞住了。
解決方案:1. 分析Socket Server爲何連不上 2. 優化改進TCPInvoker內部的業務邏輯,減小超時和重試時間,減小阻塞的產生概率。
好了,上面就是此次分享的Windbg調試線程阻塞問題的細節和過程,總結一下:
線程阻塞問題的分析思路:
周國慶
2018/11/1