多線程編程之線程死鎖問題
阿新 • • 發佈:2017-12-08
ica roc 常見 處理 帶來 模塊 現象 時間 多線程編程
在多線程編程中,除了要解決數據訪問的同步與互斥之外,還需要解決的重要問題就是多線程的死鎖問題。所謂死鎖: 是指兩個或兩個以上的進程(線程)在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外部處理作用,它們都將無限等待下去。
一、死鎖原因與形成條件
死鎖形成的原因:
- 系統資源不足
- 進程(線程)推進的順序不恰當;
- 資源分配不當
死鎖形成的條件:
- 互斥條件:所謂互斥就是進程在某一時間內獨占資源。
- 請求與保持條件:一個進程因請求資源而阻塞時,對已獲得的資源保持不放。
- 不剝奪條件:進程已獲得資源,在末使用完之前,不能強行剝奪。
- 循環等待條件:若幹進程之間形成一種頭尾相接的循環等待資源關系。
從編程經驗上來講,形成死鎖的一般原因有以下幾種:
- 個人使用鎖的經驗差異。
- 程序模塊使用鎖的差異。
- 工程代碼版本之間的差異。
- 工程代碼分支之間的差異。
- 修改代碼和重構代碼帶來的差異。
二、常見死鎖形成的場景
死鎖形成的常見情況有以下幾種:
2.1 忘記釋放鎖
void data_process() { EnterCriticalSection(); if(/* error happens, forget LeaveCriticalSection */) return; LeaveCriticalSection(); }
2.2 單線程重復申請鎖
void sub_func() { EnterCriticalSection(); do_something(); LeaveCriticalSection(); } void data_process() { EnterCriticalSection(); sub_func(); LeaveCriticalSection(); }
2.3 多線程多鎖申請
void data_process1() { EnterCriticalSection(&cs1); // 申請鎖的順序有依賴 EnterCriticalSection(&cs2); do_something1(); LeaveCriticalSection(&cs2); LeaveCriticalSection(&cs1); } void data_process2() { EnterCriticalSection(&cs2); // 申請鎖的順序有依賴 EnterCriticalSection(&cs1); do_something2(); LeaveCriticalSection(&cs1); LeaveCriticalSection(&cs2); }
2.4 環形鎖申請
/* 多個線程申請鎖的順序形成相互依賴的環形: * A - B * | | * C - D */
三、死鎖的避免策略
死鎖的代價是非常大的,有時候很難檢測排查,因此需要在編程過程中盡可能的避免發生死鎖。編程中為了避免死鎖應該遵循如下策略:
- 在編寫多線程程序之前,首先編寫正確的程序,然後再移植到多線程。
- 時刻檢查自己寫的程序有沒有在跳出時忘記釋放鎖。
- 如果自己的模塊可能重復使用一個鎖,建議使用嵌套鎖。
- 對於某些鎖代碼,不要臨時重新編寫,建議使用庫裏面的鎖,或者自己曾經編寫的鎖。
- 如果某項業務需要獲取多個鎖,必須保證鎖的按某種順序獲取,否則必定死鎖。
- 編寫簡單的測試用例,驗證有沒有死鎖。
- 編寫驗證死鎖的程序,從源頭避免死鎖。
多線程編程之線程死鎖問題