死鎖
死鎖(Deadlock)是指在并發(fā)系統(tǒng)中,兩個(gè)或多個(gè)進(jìn)程(線程)互相持有對(duì)方所需資源而無(wú)法繼續(xù)執(zhí)行的狀態(tài)。這種情況下,進(jìn)程將永遠(yuǎn)等待下去,直到外部干預(yù)才能解除死鎖。
死鎖通常涉及以下四個(gè)必要條件:
互斥條件(Mutual Exclusion):資源只能同時(shí)被一個(gè)進(jìn)程占用,其他進(jìn)程需要等待。
占有且等待(Hold and Wait):進(jìn)程在等待其他資源的同時(shí),仍然保持對(duì)已分配資源的占有。
不可搶占(No Preemption):已經(jīng)分配給一個(gè)進(jìn)程的資源不能被其他進(jìn)程搶占,只能由持有者進(jìn)程主動(dòng)釋放。
循環(huán)等待(Circular Wait):存在一個(gè)進(jìn)程鏈,每個(gè)進(jìn)程都在等待下一個(gè)進(jìn)程所持有的資源。
Case 1 : 沒有釋放鎖

Case 2 : 單線程重復(fù)申請(qǐng)鎖

Case 3 : 雙線程多鎖申請(qǐng)

避免死鎖的一般建議,就是讓兩個(gè)互斥量總以相同的順序上鎖:總在互斥量B之前鎖住互斥量 A,就永遠(yuǎn)不會(huì)死鎖。當(dāng)然前提是不同的互斥量是用于不同的地方。
當(dāng)有多個(gè)互斥量保護(hù)同一個(gè)類的獨(dú)立實(shí)例時(shí),如果一個(gè)操作需要對(duì)同一個(gè)類的兩個(gè)不同實(shí)例進(jìn)行數(shù)據(jù)交換操作,為了保證數(shù)據(jù)交換操作的正確性,需要避免數(shù)據(jù)被并發(fā)修改,并確保每個(gè)實(shí)例上的互斥量都能鎖住自己要保護(hù)的區(qū)域。
然而,此時(shí)選擇一個(gè)固定的順序來(lái)上鎖可能會(huì)導(dǎo)致問(wèn)題。例如,假設(shè)選擇第一個(gè)實(shí)例提供的互斥量作為第一個(gè)參數(shù),第二個(gè)實(shí)例提供的互斥量作為第二個(gè)參數(shù)。如果有兩個(gè)線程,它們都試圖對(duì)相同的兩個(gè)實(shí)例之間進(jìn)行數(shù)據(jù)交換,但是它們接收的參數(shù)順序不同,程序就可能會(huì)陷入死鎖狀態(tài):

std::lock
std::lock可以一次性鎖住多個(gè)互斥量,并且沒有死鎖風(fēng)險(xiǎn)。因此上述代碼可以這樣修改:

首先使用std::lock以相同的順序獲取兩個(gè)互斥量的鎖,然后使用std::adopt_lock標(biāo)志將鎖的所有權(quán)交給lock_guard。修改后的代碼確保了以相同的順序獲取鎖,從而避免了死鎖的發(fā)生。無(wú)論線程以什么樣的順序進(jìn)入swap函數(shù),都能按照相同的順序獲取鎖,避免了互相等待對(duì)方釋放鎖的情況。
std::adopt_lock
adopt_lock是一個(gè)參數(shù),用于在構(gòu)造lock_guard對(duì)象時(shí)指示它已經(jīng)擁有互斥鎖的所有權(quán)。當(dāng)lock_guard對(duì)象被創(chuàng)建時(shí),它會(huì)自動(dòng)對(duì)互斥鎖進(jìn)行上鎖,并在析構(gòu)時(shí)自動(dòng)解鎖。而使用adopt_lock參數(shù),它假設(shè)調(diào)用者已經(jīng)擁有了互斥鎖的所有權(quán),因此在構(gòu)造函數(shù)中不會(huì)嘗試再次上鎖。
一般情況下,使用lock_guard來(lái)管理互斥鎖時(shí),會(huì)在創(chuàng)建對(duì)象時(shí)自動(dòng)進(jìn)行上鎖操作。但是,在某些情況下,例如多個(gè)互斥鎖需要同時(shí)上鎖時(shí),可以先使用std::lock函數(shù)將所有的互斥鎖一起加鎖,然后再通過(guò)lock_guard對(duì)象管理這些已經(jīng)加鎖的互斥鎖。這時(shí)就可以使用adopt_lock參數(shù)告訴lock_guard對(duì)象,它已經(jīng)擁有互斥鎖的所有權(quán),無(wú)需再次上鎖。