1 死鎖的四個必要條件
(1)互斥條件: 資源是獨占的且排他使用,程序互斥使用資源,即任意時刻一個資源隻能給一個程序使用,其他程序若申請一個資源,而該資源被另一
程序占有時,則申請者等待直到資源被占有者釋放。
(2)不可搶占條件:程序所獲得的資源在未使用完畢之前,不被其他程序強行剝奪,而隻能由獲得該資源的程序資源釋放。
(3)請求和保持條件:程序已經保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他程序占有,此時請求程序被阻塞,但對自己以獲得的
資源保持不放。
(4)環等待條件:在發生死鎖時必然存在一個程序等待隊列{P1,P2,…,Pn},其中P1等待P2占有的資源,P2等待P3占有的資源,…,Pn等待P1占有的資
源,形成一個程序等待環路,環路中每一個程序所占有的資源同時被另一個申請,也就是前一個程序占有後一個程序所深情地資源。
2 處理死鎖的方法
(1)預防死鎖。該方法是通過設定某些限制條件,去破壞産生死鎖的四個必要條件中的一個或幾個來預防産生死鎖。
(2)避免死鎖。在資源的動态配置設定過程中,用某種方法防止系統進入不安全狀态,進而可以避免産生死鎖。
(3)檢測死鎖。通過檢測機構及時地檢測出死鎖的發生,然後采取适當的措施,把程序從死鎖中解脫出來。
(4)解除死鎖。當檢測到系統中已發生死鎖時,就采取相應的措施,将程序從死鎖狀态中解脫出來。常用方法是---撤銷一些程序,回收他們的資源,
将他們配置設定給已處于阻塞狀态的程序,使其能繼續運作。
3 鎖的種類:
1>、互斥鎖
互斥鎖用于控制多個線程對他們之間共享資源互斥通路的一個信号量。也就是說是為了避免多個線程在某一時刻同時操作一個共享資源。例如線程池中的有
多個空閑線程和一個任務隊列。任何是一個線程都要使用互斥鎖互斥通路任務隊列,以避免多個線程同時通路任務隊列以發生錯亂。在某一時刻,隻有一個線程
可以擷取互斥鎖,在釋放互斥鎖之前其他線程都不能擷取該互斥鎖。如果其他線程想要擷取這個互斥鎖,那麼這個線程隻能以阻塞方式進行等待。
頭檔案:<pthread.h>
類型:pthread_mutex_t,
函數:pthread_mutex_init(pthread_mutex_t * mutex, const phtread_mutexattr_t * mutexattr);//動态方式建立鎖,相當于new動态創
建一個對象
pthread_mutex_destory(pthread_mutex_t *mutex)//釋放互斥鎖,相當于delete
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//以靜态方式建立鎖
pthread_mutex_lock(pthread_mutex_t *mutex)//以阻塞方式運作的。如果之前mutex被加鎖了,那麼程式會阻塞在這裡。
pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t * mutex);//會嘗試對mutex加鎖。如果mutex之前已經被鎖定,傳回非0,;如果
mutex沒有被鎖定,則函數傳回并鎖定mutex //該函數是以非阻塞方式運作了。也就是說如果mutex之前已經被鎖定,函數會傳回非0,程式繼續往下執行。
2>、條件鎖
條件鎖就是所謂的條件變量,某一個線程因為某個條件為滿足時可以使用條件變量使改程式處于阻塞狀态。一旦條件滿足以“信号量”的方式喚醒一個因為
該條件而被阻塞的線程。最為常見就是線上程池中,起初沒有任務時任務隊列為空,此時線程池中的線程因為“任務隊列為空”這個條件處于阻塞狀态。一旦有
任務進來,就會以信号量的方式喚醒一個線程來處理這個任務。這個過程中就使用到了條件變量pthread_cond_t。
頭檔案:<pthread.h>
類型:pthread_cond_t
函數:pthread_cond_init(pthread_cond_t * condtion, const phtread_condattr_t * condattr);//對條件變量進行動态初始化,相當于
new建立對象
pthread_cond_destory(pthread_cond_t * condition);//釋放動态申請的條件變量,相當于delete釋放對象
pthread_cond_t condition = PTHREAD_COND_INITIALIZER;//靜态初始化條件變量
pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);//該函數以阻塞方式執行。如果某個線程中的程式執
行了該函數,那麼這個線程就會以阻塞方式等待,直到收到pthread_cond_signal或者pthread_cond_broadcast函數發來的信号而被喚醒。
注意:pthread_cond_wait函數的語義相當于:首先解鎖互斥鎖,然後以阻塞方式等待條件變量的信号,收到信号後又會對互斥鎖加鎖。
pthread_cond_signal(pthread_cond_t * cond);//在另外一個線程中改變線程,條件滿足發送信号。喚醒一個等待的線程(可能有多個線程處于阻塞
狀态),喚醒哪個線程由具體的線程排程政策決定
pthread_cond_broadcast(pthread_cond_t * cond);//以廣播形式喚醒所有因為該條件變量而阻塞的所有線程,喚醒哪個線程由具
體的線程排程政策決定
pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, struct timespec * time);//以阻塞方
式等待,如果時間time到了條件還沒有滿足還是會結束
3>自旋鎖
前面的兩種鎖是比較常見的鎖,也比較容易了解。下面通過比較互斥鎖和自旋鎖原理的不同,這對于真正了解自旋鎖有很大幫助。假設我們有一個兩個處理
器core1和core2計算機,現在在這台計算機上運作的程式中有兩個線程:T1和T2分别在處理器core1和core2上運作,兩個線程之間共享着一個資源。首先我
們說明互斥鎖的工作原理,互斥鎖是是一種sleep-waiting的鎖。假設線程T1擷取互斥鎖并且正在core1上運作時,此時線程T2也想要擷取互斥鎖
(pthread_mutex_lock),但是由于T1正在使用互斥鎖使得T2被阻塞。當T2處于阻塞狀态時,T2被放入到等待隊列中去,處理器core2會去處理其他任務
而不必一直等待(忙等)。也就是說處理器不會因為線程阻塞而空閑着,它去處理其他事務去了。
而自旋鎖就不同了,自旋鎖是一種busy-waiting的鎖。也就是說,如果T1正在使用自旋鎖,而T2也去申請這個自旋鎖,此時T2肯定得不到這個自旋鎖。
與互斥鎖相反的是,此時運作T2的處理器core2會一直不斷地循環檢查鎖是否可用(自旋鎖請求),直到擷取到這個自旋鎖為止。從“自旋鎖”的名字也可以看
出來,如果一個線程想要擷取一個被使用的自旋鎖,那麼它會一緻占用CPU請求這個自旋鎖使得CPU不能去做其他的事情,直到擷取這個鎖為止,這就是“自旋”
的含義。當發生阻塞時,互斥鎖可以讓CPU去處理其他的任務;而自旋鎖讓CPU一直不斷循環請求擷取這個鎖。通過兩個含義的對比可以我們知道“自旋鎖”
是比較耗費CPU的
頭檔案:<linux\spinlock.h>
自旋鎖的類型:spinlock_t
相關函數:初始化:spin_lock_init(spinlock_t *x);
spin_lock(x); //隻有在獲得鎖的情況下才傳回,否則一直“自旋”
spin_trylock(x); //如立即獲得鎖則傳回真,否則立即傳回假
釋放鎖:spin_unlock(x);
spin_is_locked(x)// 該宏用于判斷自旋鎖x是否已經被某執行單元保持(即被鎖),如果是, 傳回真,否則傳回假。
4> 讀寫鎖
讀寫鎖比起mutex具有更高的适用性,具有更高的并行性,可以有多個線程同時占用讀模式的讀寫鎖,但是隻能有一個線程占用寫模式的讀寫鎖,讀寫鎖
的三種狀态:
1.當讀寫鎖是寫加鎖狀态時,在這個鎖被解鎖之前,所有試圖對這個鎖加鎖的線程都會被阻塞
2.當讀寫鎖在讀加鎖狀态時,所有試圖以讀模式對它進行加鎖的線程都可以得到通路權,但是以寫模式對它進行加鎖的線程将會被阻塞
3.當讀寫鎖在讀模式的鎖狀态時,如果有另外的線程試圖以寫模式加鎖,讀寫鎖通常會阻塞随後的讀模式鎖的請求,這樣可以避免讀模式鎖長期占用,而等待
的寫模式鎖請求則長期阻塞。
讀寫鎖最适用于對資料結構的讀操作次數多于寫操作的場合,因為,讀模式鎖定時可以共享,而寫模式鎖定時隻能某個線程獨占資源,因而,讀寫鎖也可
以叫做個共享-獨占鎖。