什麼是 Deadlock#

Deadlock(死結)是多個執行緒互相等待對方持有的資源,導致所有執行緒都無法繼續執行的狀態。經典場景:兩個執行緒各需要鎖定兩個共享資料結構,若其中一個鎖了 A 等 B,另一個鎖了 B 等 A,就會陷入死結。

Bryan Cantrill 和 Jeff Bonwick 在 ACM Queue 的文章 “Real-world Concurrency” 中指出,deadlock 其實是最容易分析的並行錯誤之一——因為系統是凍結的,你可以從容地取得快照來分析。

事後分析的步驟#

C/C++ 程式(使用 gdb)#

  1. 取得 core dump:找到凍結程式的 PID,用 kill -QUIT 產生 core dump
  2. 載入 gdb:指定執行檔和 core dump 檔案
    gdb deadlock core
  3. 列出執行緒:用 info threads 查看所有執行緒的狀態
  4. 逐一檢查:用 thread N 切換到等待中的執行緒,再用 backtrace 查看 stack frame
  5. 定位程式碼:從 stack trace 中找到你的程式碼(而非系統函式庫)中嘗試取得鎖的位置

Java 程式(使用 jstack)#

  • Java 的 jstack 工具更為方便,直接就能顯示 deadlock 資訊
  • jps 找到 process ID,再執行 jstack -l <PID>
  • jstack 會自動偵測並報告 deadlock,包括哪些執行緒在等待哪些 monitor,以及對應的程式碼位置
$ jps
5504 Deadlock
$ jstack -l 5504

處理 Deadlock 的策略#

當確認系統可能發生 deadlock 後,需要了解系統設計中如何處理此問題:

  • 忽略(ignoring)
  • 偵測(detecting)
  • 預防(preventing)
  • 避免(avoiding)

常見的預防手法#

  • 減少鎖的數量
  • 避免 reentrancy(可重入性)
  • 建立 lock hierarchy(鎖定階層):以 partial ordering 確保所有執行緒以相同順序取得鎖
  • 使用 try-lock 而非 blocking lock,讓鎖定失敗時可以退回

最實用的預防方式是資源排序(partial ordering)——確保所有執行緒永遠以相同順序取得鎖。例如範例中只要讓 alice 也先鎖 m1 再鎖 m2,deadlock 就不會發生。

重點回顧#

  • Deadlock 發生時系統是凍結的,可以透過 core dump 取得快照進行事後分析
  • 使用 debugger 的 info threadsbacktrace 定位每個等待中執行緒嘗試取得鎖的程式碼位置
  • Java 的 jstack 能自動偵測並報告 deadlock 及相關的 stack trace
  • 確認 deadlock 後,透過資源排序、減少鎖數量或使用 try-lock 等方式預防