1. 程式人生 > >如何檢查線程是否死鎖了?

如何檢查線程是否死鎖了?

版本 保持 條件 應用程序 ati 輸出 動態 pstack 系統資源

產生死鎖的四個必要條件

(1) 互斥條件:一個資源每次只能被一個進程(線程)使用。
(2) 請求與保持條件:一個進程(線程)因請求資源而阻塞時,對已獲得的資源保持不放。
(3) 不剝奪條件 : 此進程(線程)已獲得的資源,在末使用完之前,不能強行剝奪。
(4) 循環等待條件 : 多個進程(線程)之間形成一種頭尾相接的循環等待資源關系。

可以使用 jstack或者pstack 和 gdb 工具對死鎖程序進行分析。

pstack: 功能是打印輸出此進程的堆棧信息。可以輸出所有線程的調用關系棧

jstack:jstack是java虛擬機自帶的一種堆棧跟蹤工具,所以僅適用於java程序,功能跟pstack一樣,但是更強大,可以提示哪個地方可能死鎖了。

pstack和jstack判斷死鎖,都需要多執行幾次命令,觀察每次的輸出結果,才能推測是否死鎖了。

gdb

1 運行程序,設置能影響程序運行的參數和環境 ;

2 控制程序在指定的條件下停止運行;

3 當程序停止時,可以檢查程序的狀態;

4 當程序 crash 時,可以檢查 core 文件;

5 可以修改程序的錯誤,並重新運行程序;

6 可以動態監視程序中變量的值;

7 可以單步執行代碼,觀察程序的運行狀態。

線程死鎖分析:

1. 連續多次執行 $pstack <PID> 其中PID是進程號

查看每個線程的函數調用關系的堆棧,觀察每個線程當前的執行點是否在等待一個鎖。

多次執行該命令,發現某些線程的當前執行點不變,總是在等待同一個鎖,就可以懷疑是否死鎖了。

如果懷疑哪些線程發生死鎖了,可以采用gdb 進一步attach線程並進行分析。

2. 執行$gdb -p <PID> 或者 $gdb attach <PID>

(gdb) info thread

(gdb) thread <thread ID>

BTW:gdb的all-stop和non-stop模式 (GDBv7.0及之上版本支持)

默認都是all-stop,即在某線程的代碼裏設置了斷點,觸發斷點時所有的線程都會中斷。

non-stop是多線程調試的好幫手,某個線程裏的斷點觸發了,其它線程還照常run,可以重現死鎖的場景。

想打開non-stop,可以修改~/.gdbinit,添加如下三行:

set target-async 1
set pagination off
set non-stop on

或者在進入gdb以後,依次執行以上三行,可以臨時開啟non-stop,退出gdb後失效。(猜是這樣,需要實驗驗證)

另外,如果多個線程走的一個代碼塊,停在了同一個斷點上,例如只想讓線程1和線程3繼續,使用:

(gdb) thread apply 3 1 continue

BTW,判斷熱鎖。熱鎖是指經常被打開關閉打開關閉的鎖,由於多個線程對鎖或者臨界區的競爭造成的。

* 頻繁的線程的上下文切換:從操作系統對線程的調度來看,當 線程在等待資源而阻塞的時候,操作系統會將之切換出來,放到等待的隊列,當線程獲得資源之後,調度算法會將這個線程切換進去,放到執行隊列中。
* 大量的系統調用:因為線程的上下文切換,以及熱鎖的競爭,或 者臨界區的頻繁的進出,都可能導致大量的系統調用。
* 大部分 CPU開銷用在 “系統態 ”:線程上下文切換,和系統調用,都會導致 CPU在 “系統態 ”運行,換而言之,雖然系統很忙碌,但是 CPU用在 “用戶態 ”的比例較小,應用程序得不到充分的 CPU資源。
* 隨著 CPU數目的增多,系統的性能反而下降。因為 CPU數目多,同 時運行的線程就越多,可能就會造成更頻繁的線程上下文切換和系統態的 CPU開銷,從而導致更糟糕的性能。

可以通過腳本來固定時間間隔調用pstack,和查看系統資源使用情況的命令,比如top,分別將輸出打印到log文件裏。

當然要每次執行命令都得打印時間戳。

分析log文件,可以大概推測出熱鎖所在的位置。可以通過優化熱鎖,來提升程序的性能,例如吞吐量、響應時間。

如何檢查線程是否死鎖了?