同步機制——互斥體鎖、讀/寫鎖、信號量鎖、條件變量

(區別於linux內核所用的自旋鎖和互斥鎖,本文中討論的鎖用於普通編程)

當兩個或多個併發線程的執行次序造成了意想不到的錯誤結果時,“競態條件”就是會產生。防止“競態條件”的一個方法是使用同步機制,對訪問“共享資源”的代碼中關鍵段實施“品行訪問”控制。常用的OS同步機制有:互斥體(mutex)、“多讀取者/單寫入者”鎖(reader/writer locks)、信號量(semaphores)和條件變量(condition variable)。


1.互斥體(mutex,Mutual Exclusion)鎖
當共享資源被多個線程併發訪問時,爲了確保這些資源的完整性,我們可以使用互斥體(mutex)鎖。互斥體可用來串行執行多個線程,這需要在代碼中確定關鍵 (critical section)——即,一次只能由一個線程執行的代碼。“互斥體”語義像雙括號那樣具有“對稱體”:也就是就說,如果一個線程擁有互斥體,那麼,它還和負責釋放這個互斥體。這種簡單的語義有助於互斥體的高效實現——如通過硬件自旋鎖(spinlock)來實現。

常見的互斥體有兩種:
非遞歸互斥體(nonrecursive mutex):如果當前擁有互斥體的線程在沒有首先釋放它的情況,試圖再次獲得它,就會導致死鎖或失敗。
遞歸互斥體(recursive mutex):擁有互斥體的線程可能多次獲得它而不會產生自死鎖,只要這個線程最終以相同次數釋放這個互斥體即可。

2.Readers/Writer鎖
Readers/Writer(多讀取者/單寫入者)鎖可以通過以下方式之一訪問共享資源:
多個線程併發讀取資源,但不修改它;
一次只一個線程修改資源,此時其它線程都不能對其進行讀/寫訪問。

Readers/Writer鎖可以用來保護“讀操作比寫操作頻繁”的資源,從而提高併發應用程序的性能。在實現Readers/Writer鎖時,要麼給“讀取者”以優先權,要麼給“寫入者”以優先權。

Readers/Writer 鎖和互斥體有某些共性,例如:獲得鎖的線程也必須釋放這個鎖,如查一個“寫入者”希望獲得這個鎖,那麼,這個線程必須等待其它所有“擁有這個鎖”的線程釋放它;然後,這個“寫入者”線程單獨佔有這個鎖。便但和互斥體不同的是,多個線程可以同時獲得一個Readers/Writer鎖執行“讀”操作。

3.信號量鎖
從概念上說,信號量(semaphore)是可以原子(automically)遞增和背叛的非負整數。如果一個線程試圖遞減一個信號量,但這個信號量的值已經爲0,則線程將會阻塞。另一個線程“發出(post)”這個信號(semaphore),使用信號量大於0之後,被阻塞的線程纔會被釋放。

信號量維護狀態信息,對信號計數值(count)和被阻塞線程的數量進行記錄。它們一般是通過“休止鎖(sleep lock)”來實現的;休止鎖用來觸發環境切換,以允許其他線程執行。和互斥體不同的是,釋放信號量的線程不必是最初獲得這個信號量的線程。這使得信號量適用於更廣泛的執行環境,如信號處理程序或中斷處理程序。

4.條件變量
和互斥體、Readers/Writer鎖、信號量鎖不同,條件變量(condtion variable)提供了不同風格的同步方式。在前三種機制中,當“佔有鎖的線程”在關鍵段中執行代碼時,其他線程會等待。下此相反,使用條件變量,線程可以調整和調度自己的處理過程。

例如,某一數據被其他線程共享;條件變量可能使用處於等待狀態,直至“涉及這個數據”的一個條件表達式達到某一狀態。當一個“合作線程(cooperation thread)”顯示共享數據的狀態已經改變時,阻塞在條件上的線程會被喚醒;然後,被喚醒的線程重新計算它的條件表達式,如果共享數據達到預期狀態,則恢復處理。再複雜的條件表達式也可以通過條件變量來等待;所以,較之前面所說的同步機制,條件變量允許更復雜的調度決策。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章