死鎖產(chǎn)生的條件有四個
1.互斥(mutual exclusion): 訪問的資源必須是非共享的觉阅,A與B的訪問要是互斥的,其他訪問要等待
2.占有并等待(hold and wait):A占有一個資源并等待B占有的另一個資源
3.非搶占(no preemption):A占有的資源只有A自己去釋放称龙,不能被奪走
4.循環(huán)等待(circular wait):類似于哲學(xué)家就餐問題(A等待B占有的資源留拾,B等待C占有的資源,C等待A占有的資源)- 必要條件(比如B占有的資源有多個實例鲫尊,那么另一個需要相同的資源的就不會因為B占有了而出現(xiàn)死鎖問題)
最終就是表現(xiàn)為資源的互相等待的循環(huán)
我們知道死鎖產(chǎn)生了有一些方法去避免痴柔,下面這個問題就是通過加鎖的順序來避免死鎖
指定加鎖的順序并不能真正的避免死鎖因為調(diào)用的資源可能是通過外界傳入進來的順序,比如銀行賬戶的每個賬戶都有一個鎖疫向,下面的方式通過鎖的順序是會出現(xiàn)死鎖的
// 這些是偽代碼
type Account struct {
sync.Mutex
nMoney uint64
nAccountID uint64
}
// one to one
func TranToHashCode(account Account) int {
return 0
}
// 取款
func WithDraw(account Account, nAmount uint64) {
}
// 存款
func Deposit(account Account, nAmount uint64) {
}
// 產(chǎn)生死鎖的方式
// 如果有兩個攜程調(diào)用的話咳蔚,并且調(diào)用順序為
// Transaction(from, to,10)
// Transaction(to, from,10)
// 由于并發(fā)導(dǎo)致的交叉執(zhí)行會產(chǎn)生死鎖的可能
func Transaction(from Account, to Account, aMount uint64) {
from.Lock()
to.Lock()
WithDraw(from, aMount)
Deposit(to, aMount)
to.Unlock()
from.Unlock()
}
一種解決方案是認為的控制調(diào)用鎖的順序
// 解決死鎖的方式
// 我們 通過給每個賬號求一個hash值人為的改變順序
func Transaction2(from Account, to Account, aMount uint64) {
var fromHashCode int = TranToHashCode(from)
var toHashCode int = TranToHashCode(to)
if fromHashCode < toHashCode {
from.Lock()
to.Lock()
WithDraw(from, aMount)
Deposit(to, aMount)
to.Unlock()
from.Unlock()
return
}
to.Lock()
from.Lock()
WithDraw(from, aMount)
Deposit(to, aMount)
from.Unlock()
to.Unlock()
}
上面這些都是偽代碼,作為示例只是說明
舉個mysql的例子
mysql也會有死鎖的問題搔驼,當(dāng)然原因肯定也是這四個條件滿足才會出現(xiàn)谈火,比如存儲引擎innob批量更新數(shù)據(jù)庫會有行鎖磕洪,如果有兩個更新任務(wù)里面有交叉的更新任務(wù)比如A[1,2,3,4] B[6,5,2,1]就有可能出現(xiàn)死鎖問題 滿足上面四個條件
第一逐虚、行鎖 有相同的行會有互斥
第二休玩、如果交叉執(zhí)行A執(zhí)行了1然后B去執(zhí)行了6,5,2 緊接著又A開始執(zhí)行就就先占有等待的問題
第三巨柒、因為A任務(wù)不會自己釋放任務(wù),就是等待執(zhí)行完才會釋放
第四捌显、因為第二條的占有等待 A等待B B等待A 一直循環(huán)等待也就思索了
如果我們的update處理把更新序列排序也就是A[1,2,3,4] B[1,2,5,6]死鎖就可以解決
因此預(yù)防可以從這四個必要條件去著手-----缺點是或?qū)е滦阅軉栴}