1. 程式人生 > >Windbg程式除錯系列3-執行緒阻塞問題

Windbg程式除錯系列3-執行緒阻塞問題

上一篇博文給大家分享了使用Windbg分析記憶體洩露問題:

Windbg程式除錯系列2-記憶體洩露問題

本篇我們繼續跟大家分享,如何分析解決執行緒阻塞問題。

從根本上講,執行緒阻塞屬於程式Hang的一種,其表現主要有:

1. 隨著請求的增加,執行緒數一直增加,可能會把執行緒池打爆

2. 低CPU使用率(被阻塞後的CPU使用率降低)

3. 請求沒有返回,客戶端一直在等待,直至Timeout。

 那麼,從執行緒狀態上看,什麼是阻塞? 一個執行緒經歷的5個狀態,建立,就緒,執行,阻塞,終止。各個狀態的轉換條件如下圖:

上圖中有個阻塞狀態,就是說當執行緒中呼叫某個函式,需要IO請求,或者暫時得不到競爭資源的,作業系統會把該執行緒阻塞起來,避免浪費CPU資源,等到得到了資源,再變成就緒狀態,等待CPU排程執行。

執行緒發生阻塞的現象就是,程序的執行緒數會越來越多!

執行緒阻塞問題的分析思路:

持續請求過程中,抓兩個或者三個Dump,看執行緒增加的速度,每次抓Dump間隔30s或者1分鐘
對比的看每個Dump中:

  1. 執行緒池的大小 !threadpool
  2. 執行緒的分類和狀態 !threads
  3. 檢視執行緒阻塞 !syncblk
  4. 檢視阻塞執行緒的根源執行緒、執行緒請求的資源物件 、被阻塞的執行緒數
  5. 檢視阻塞根源執行緒的堆疊~Xs !clrstack
  6. 分析執行緒阻塞的原因,改進程式碼

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除錯執行緒阻塞問題的細節和過程,總結一下:

執行緒阻塞問題的分析思路:

  1. 執行緒池的大小 !threadpool
  2. 執行緒的分類和狀態 !threads
  3. 檢視執行緒阻塞 !syncblk
  4. 檢視阻塞執行緒的根源執行緒、執行緒請求的資源物件 、被阻塞的執行緒數
  5. 檢視阻塞根源執行緒的堆疊~Xs !clrstack
  6. 分析執行緒阻塞的原因,改進程式碼

周國慶

2018/11/1