http://blog.csdn.net/gebushuaidanhenhuai/article/details/73799824
線程為什會死鎖??“鎖”又是什么東西?我們這篇博客主要講一下為什么要給線程加鎖,為什么會出現線程死鎖,線程死鎖怎么解決。
互斥鎖
在我的上篇博客已經講解了一些線程的基本知識Linux–線程控制我們可以了解到線程是共享同一份內存的。這就意味著多個線程同時訪問共享數據時可能發生沖突。
分析程序
我們首先分析一個小程序:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
上面程序是創建兩個線程,各自都把全局靜態變量count增加5000次。正常情況下結果應該是10000,但是程序的最終結果總是5000左右,與正確結果相差很遠,這是為什么呢??
這是因為對多線程的程序,發生了訪問沖突了。對一個線程要對一個變量進行加1操作,需要進行三步:?
1.將數據從內存單元讀入寄存器。?
2.把寄存器中的變量做加1操作。?
3.把加1后的變量重新寫回內存單元。
我們來模擬一下上面程序的運行。我們上面的程序有兩個線程tid1,tid2。假如此時變量count = 10.?
當tid1進行第二步時,從內存讀到10,在寄存器加1–>10+1=11。?
但是此時tid2切入,從內存讀到10,在寄存器加1–>10+1=11。?
因為tid1還沒有把11寫入到內存中,tid2讀到的值并不是我們想要的11,這就引發了問題。?
解決的方法就是引入互斥鎖,獲得鎖線程可以完成“讀–修改–寫”的操作,然后釋放鎖給其他線程,沒有獲得鎖的線程只能等待而不能訪問共享數據。這樣“讀–修改–寫”散步操作組成了一個原子操作,要么都執行,要么都不執行。
互斥鎖操作函數
互斥鎖的數據類型為pthread_mutex_t.?
創建互斥鎖:
- 1
- 2
返回值:成功返回0,失敗返回錯誤號。?
參數:?
pthread_mutex_init()函數是以動態方式創建互斥鎖的,參數attr指定了新建互斥鎖的屬性。如果參數attr為空,則使用默認的互斥鎖屬性,默認屬性為快速互斥鎖 。?
如果Mutex變量是靜態分配的,也可以利用宏定義來初始化。
- 1
此方法相當于pthread_mutex_init初始化并且attr參數為NULL.?
銷毀互斥鎖:
- 1
- 2
返回值:成功返回0,失敗返回錯誤碼。?
加鎖:
- 1
- 2
解鎖:
- 1
- 2
返回值:成功返回0,失敗返回錯誤號。?
一個線程可以調用pthread_mutex_lock還會獲得Mutex,若果這時另一個線程已經調用pthread_mutex_lock獲得了該Mutex,則當前進程需要掛起等待,直到另一個線程調用pthread_mutex_unlock釋Mutex,當前進程被喚醒,才能獲得該Mutex并繼續執行。?
如果一個線程既想獲得鎖,又不想掛起等待,可以調用pthread_mutex_trylock,如果Mutex已經被另一個線程獲得,這個函數會失敗返回EBUSY,而不會使線程掛起等待。
- 1
- 2
這時我們就可以解決上面那個代碼的bug了。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
線程死鎖
?般情況下,如果同?個線程先后兩次調?lock,在第?次調?時,由于鎖已經被占?,該線程會掛起等待別的線程釋放鎖,然?鎖正是被??占?著的,該線程又被掛起?沒有機會釋放鎖,因此 就永遠處于掛起等待狀態了,這叫做死鎖(Deadlock)。?
另一種情況:線程A獲 得了鎖1,線程B獲得了鎖2,這時線程A調?lock試圖獲得鎖2,結果是需要掛起等待線程B釋放 鎖2,?這時線程B也調?lock試圖獲得鎖1,結果是需要掛起等待線程A釋放鎖1,于是線程A和B都 永遠處于掛起狀態了。?
死鎖產生的4個必要條件:?
1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用畢釋放。?
2)請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。?
3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。?
4)環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。?
預防死鎖:?
只要打破四個必要條件之一就能有效預防死鎖的發生:打破互斥條件:改造獨占性資源為虛擬資源,大部分資源已無法改造。?
打破不可搶占條件:當一進程占有一獨占性資源后又申請一獨占性資源而無法滿足,則退出原占有的資源。?
打破占有且申請條件:采用資源預先分配策略,即進程運行前申請全部資源,滿足則運行,不然就等待,這樣就不會占有且申請。?
打破循環等待條件:實現資源有序分配策略,對所有設備實現分類編號,所有進程只能采用按序號遞增的形式申請資源。
有序資源分配法
這種算 法資源按某種規則系統中的所有資源統一編號(例如打印機為1、磁帶機為2、磁盤為3、等等),申請時必須以上升的次序。系統要求申請進程:?
1、對它所必須使用的而且屬于同一類的所有資源,必須一次申請完;?
2、在申請不同類資源時,必須按各類設備的編號依次申請。例如:進程PA,使用資源的順序是R1,R2; 進程PB,使用資源的順序是R2,R1;若采用動態分配有可能形成環路條件,造成死鎖。?
采用有序資源分配法:R1的編號為1,R2的編號為2;?
PA:申請次序應是:R1,R2?
PB:申請次序應是:R1,R2?
這樣就破壞了環路條件,避免了死鎖的發生
銀行家算法
避免死鎖算 法中最有代表性的算 法是Dijkstra E.W 于1968年提出的銀行家 算 法:?
銀行家算法是避免死鎖的一種重要方法,防止死鎖的機構只能確保上述四個條件之一不出現,則系統就不會發生死鎖。通過這個算法可以用來解決生活中的實際問題,如銀行貸款等。?
程序實現思路銀行家算法顧名思義是來源于銀行的借貸業務,一定數量的本金要應多個客戶的借貸周轉,為了防止銀行家資金無法周轉而倒閉,對每一筆貸款,必須考察其是否能限期歸還。在操作 系統中研究資源分配策略時也有類似問題,系統中有限的資源要供多個進程使用,必須保證得到的資源的進程能在有限的時間內歸還資源,以供其他進程使用資源。如果資源分配不得到就會發生進程循環等待資源,則進程都無法繼續執行下去的死鎖現象。?
把一個進程需要和已占有資源的情況記錄在進程控制中,假定進程控制塊PCB其中“狀態”有就緒態、等待態和完成態。當進程在處于等待態時,表示系統不能滿足該進程當前的資源申請。“資源需求總量”表示進程在整個執行過程中總共要申請的資源量。顯然,每個進程的資源需求總量不能超過系統擁有的資源總數, 銀行算法進行資源分配可以避免死鎖。