遊戲 Lag 白皮書 › L9 伺服器遊戲程式
死結 Deadlock
原因 ID sp-deadlock · 主要負責 遊戲開發團隊(伺服器開發)
在含圖解與實驗的完整版中開啟卡片 →
兩個執行緒各自等待對方持有的鎖時,就會永遠停住。
為什麼 執行緒 A 持有鎖 1 並等待鎖 2,B 持有鎖 2 並等待鎖 1 → 於是 兩者都永遠停住,相關的執行緒也接連停住 → 畫面上 整台伺服器停止運作,看門狗(watchdog)重新啟動時所有人斷線
- 症狀
- 定格, 斷線
- 因素
- 停滯
- 誰會遇到
- 整個伺服器, 只有特定功能
- 何時
- 偶爾隨機發生, 人潮湧入時
- 負責單位
- 主要負責 遊戲開發團隊(伺服器開發)
- 遊戲開發團隊要做的事
- 制定取鎖順序規則、使用有逾時的鎖、加入看門狗並在停住的瞬間留下 thread dump。
- 圖表上
- 連線同時大量中斷 · 連線數、伺服器傳送量
- 查看位置
- 停住期間擷取所有執行緒的呼叫堆疊。JVM 用 jstack(會自動找出並標示死結),.NET 用 dotnet-stack,原生伺服器用 gdb 的 thread apply all bt,或先用 gcore 擷取 core 檔案,重新啟動後再分析
- 符合的跡象
- 兩個以上的執行緒停在等待對方所持有鎖的堆疊上,這段期間處理程序的 CPU 使用率接近 0
- 不符合的跡象
- 停住期間有一個執行緒以 CPU 100% 在跑時是無窮迴圈。執行緒都在等 DB 或外部回應時,是同步呼叫或執行緒池耗盡
- 確認方式
- 用基礎設施工具確認(不需要遊戲程式碼)
出處
- Runtime locking correctness validator Linux kernel
以相反順序取得兩個鎖時,會因循環等待而死結(lock inversion deadlock);Linux kernel 會檢查取鎖順序並事先警告 - Liveness, Readiness, and Startup Probes Kubernetes
以 liveness probe 偵測「仍在執行卻無法前進」的死結狀態,並重新啟動容器 - Diagnostic Tools (Java SE 21 Troubleshooting Guide) Oracle
jstack 會輸出執行中 JVM 所有執行緒的堆疊,也會找出並標示死結(Found one Java-level deadlock) - dotnet-stack diagnostic tool - .NET CLI Microsoft
擷取並輸出 .NET 處理程序所有執行緒的 managed 堆疊 - Threads (Debugging with GDB) GNU Project
以 thread apply all 對所有執行緒執行同一個指令(bt:輸出呼叫堆疊) - gcore(1) — Linux manual page gdb
為執行中的程式產生 core 檔案,產生後程式仍繼續執行
相關原因
同一層:L9 伺服器遊戲程式
同一症狀(定格)在其他層的原因
查看含圖解與實驗的完整版卡片