死鎖的產生以及避免原理和演算法
死鎖 是指多個併發程序在執行過程中因競爭資源而造成的一種僵局,當程序處於這種僵持狀態時,若無外力作用,這些程序都無法向前推進。
- 併發性 : 指兩個或多個事件在同一時刻發生
- 並行性 : 指兩個或多個事件在同一時間間隔內發生
來看兩種典型的死鎖情形:
- 如果同⼀個執行緒先後兩次調⽤lock,在第⼆次調⽤時,由於鎖已經被佔⽤,該執行緒會掛起等待別的執行緒釋放鎖,然⽽鎖正是被⾃⼰佔⽤著的,該執行緒又被掛起⽽沒有機會釋放鎖,因此 就永遠處於掛起等待狀態了。
- 執行緒A獲得了鎖1,執行緒B獲得了鎖2,這時執行緒A調⽤lock試圖獲得鎖2,結果是需要掛起等待執行緒B釋放 鎖2,⽽這時執行緒B也調⽤lock試圖獲得鎖1,結果是需要掛起等待執行緒A釋放鎖1,於是執行緒A和B都 永遠處於掛起狀態了。
產生死鎖的原因
由上述兩種情形,可知,產生死鎖的原因如下:
1. 系統資源不足
2. 程序(執行緒)推進的順序不當;
3. 資源分配不當
產生死鎖的必要條件
程序(執行緒)執行過程中可能發生死鎖,但死鎖的產生也需要具備必要的條件:
(1)互斥條件:一個資源每次只能被一個程序使用。
(2)請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。
(3)不可剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪。
(4)迴圈等待條件:若干程序之間形成一種頭尾相接的迴圈等待資源關係。
如果涉及到更多的執行緒和更多的鎖,可能死鎖的問題將會變得複雜和難以判斷。
寫程式時應該儘量避免同時獲得多個鎖,如果⼀定有必要這麼做,則有⼀個原則 : 如果所有執行緒在需要多個鎖時都按相同的先後順序(常見的是按Mutex變數的地址順序)獲得鎖,則不會出現死鎖。⽐如⼀個程式中⽤到鎖1、鎖2、鎖3,它們所對應的Mutex變數的地址是鎖1<鎖2<鎖3, 那麼所有執行緒在需要同時獲得2個或3個鎖時都應該按鎖1、鎖2、鎖3的順序獲得。如果要為所有的鎖確定⼀個先後順序⽐較困難,則應該儘量使⽤pthread_mutex_trylock(該函式申請mutex失敗時返回EBUSY)調⽤代替pthread_mutex_lock(該函式申請mutex失敗時直接掛起等待,直到被喚醒) 調⽤,以免死鎖。
死鎖的避免
前面提到死鎖的產生需要滿足其必要條件,那麼死鎖的避免就要破壞其必要條件。
* 注意:死鎖的互斥條件不可破壞,程序(執行緒)對臨界資源的訪問必須保證互斥。
1. 摒棄“請求和保持”條件
2. 摒棄“不剝奪”條件
3. 摒棄“環路等待”條件
基本思想 :
允許三個必要條件存在。系統在進行資源分配之前,應先計算此次資源分配後狀態的安全性。若此次分配後的狀態是安全狀態,則將資源分配給程序;否則,令程序等待。
避免死鎖的演算法:
銀行家演算法
銀行家演算法
演算法原理
我們可以把作業系統看作是銀行家,作業系統管理的資源相當於銀行家管理的資金,程序向作業系統請求分配資源相當於使用者向銀行家貸款。
為保證資金的安全,銀行家規定:
- 當一個顧客對資金的最大需求量不超過銀行家現有的資金時就可接納該顧客;
- 顧客可以分期貸款,但貸款的總數不能超過最大需求量;
- 當銀行家現有的資金不能滿足顧客尚需的貸款數額時,對顧客的貸款可推遲支付,但總能使顧客在有限的時間裡得到貸款;
- 當顧客得到所需的全部資金後,一定能在有限的時間裡歸還所有的資金.