1. 程式人生 > >自旋鎖 互斥鎖 讀寫鎖 遞迴鎖

自旋鎖 互斥鎖 讀寫鎖 遞迴鎖

互斥鎖(mutexlock):

最常使用於執行緒同步的鎖;標記用來保證在任一時刻,只能有一個執行緒訪問該物件,同一執行緒多次加鎖操作會造成死鎖;臨界區和互斥量都可用來實現此鎖,通常情況下鎖操作失敗會將該執行緒睡眠等待鎖釋放時被喚醒

自旋鎖(spinlock):

同樣用來標記只能有一個執行緒訪問該物件,在同一執行緒多次加鎖操作會造成死鎖;使用硬體提供的swap指令或test_and_set指令實現;同互斥鎖不同的是在鎖操作需要等待的時候並不是睡眠等待喚醒,而是迴圈檢測保持者已經釋放了鎖,這樣做的好處是節省了執行緒從睡眠狀態到喚醒之間核心會產生的消耗,在加鎖時間短暫的環境下這點會提高很大效率

讀寫鎖(rwlock):

高級別鎖,區分讀和寫,符合條件時允許多個執行緒訪問物件。處於讀鎖操作時可以允許其他執行緒和本執行緒的讀鎖, 但不允許寫鎖, 處於寫鎖時則任何鎖操作都會睡眠等待;常見的作業系統會在寫鎖等待時遮蔽後續的讀鎖操作以防寫鎖被無限孤立而等待,在作業系統不支援情況下可以用引用計數加寫優先等待來用互斥鎖實現。 讀寫鎖適用於大量讀少量寫的環境,但由於其特殊的邏輯使得其效率相對普通的互斥鎖和自旋鎖要慢一個數量級;值得注意的一點是按POSIX標準 線上程申請讀鎖並未釋放前本執行緒申請寫鎖是成功的,但執行後的邏輯結果是無法預測

遞迴鎖(recursivelock):

嚴格上講遞迴鎖只是互斥鎖的一個特例,同樣只能有一個執行緒訪問該物件,但允許同一個執行緒在未釋放其擁有的鎖時反覆對該鎖進行加鎖操作; windows下的臨界區預設是支援遞迴鎖的,而linux下的互斥量則需要設定引數PTHREAD_MUTEX_RECURSIVE_NP,預設則是不支援

大讀者鎖是讀寫鎖的高效能版,讀者可以非常快地獲得鎖,但寫者獲得鎖的開銷比較大。大讀者鎖只存在於2.4核心中,在2.6中已經沒有這種鎖(提醒讀者特別注意)。它們的使用與讀寫鎖的使用類似,只是所有的大讀者鎖都是事先已經定義好的。這種鎖適合於讀多寫少的情況,它在這種情況下遠好於讀寫鎖。

大讀者鎖的實現機制是:每一個大讀者鎖在所有CPU上都有一個本地讀者寫者鎖,一個讀者僅需要獲得本地CPU的讀者鎖,而寫者必須獲得所有CPU上的鎖。

大讀者鎖的API非常類似於讀寫鎖,只是鎖變數為預定義的鎖ID。

大核心鎖本質上也是自旋鎖,但是它又不同於自旋鎖,自旋鎖是不可以遞迴獲得鎖的,因為那樣會導致死鎖。但大核心鎖可以遞迴獲得鎖。大核心鎖用於保護整個核心,而自旋鎖用於保護非常特定的某一共享資源。

程序保持大核心鎖時可以發生排程,具體實現是:在執行schedule時,schedule將檢查程序是否擁有大核心鎖,如果有,它將被釋放,以致於其它的程序能夠獲得該鎖,而當輪到該程序執行時,再讓它重新獲得大核心鎖。注意在保持自旋鎖期間是不執行發生排程的。

需要特別指出,整個核心只有一個大核心鎖,其實不難理解,核心只有一個,而大核心鎖是保護整個核心的,當然有且只有一個就足夠了。

還需要特別指出的是,大核心鎖是歷史遺留,核心中用的非常少,一般保持該鎖的時間較長,因此不提倡使用它。從2.6.11核心起,大核心鎖可以通過配置核心使其變得可搶佔(自旋鎖是不可搶佔的),這時它實質上是一個互斥鎖,使用訊號量實現。

RCU(Read-Copy Update),顧名思義就是讀-拷貝修改,它是基於其原理命名的。對於被RCU保護的共享資料結構,讀者不需要獲得任何鎖就可以訪問它但寫者在訪問它時首先拷貝一個副本,然後對副本進行修改,最後使用一個回撥(callback)機制在適當的時機把指向原來資料的指標重新指向新的被修改的資料。這個時機就是所有引用該資料的CPU都退出對共享資料的操作。

RCU也是讀寫鎖的高效能版本,但是它比大讀者鎖具有更好的擴充套件性和效能。 RCU既允許多個讀者同時訪問被保護的資料,又允許多個讀者和多個寫者同時訪問被保護的資料(注意:是否可以有多個寫者並行訪問取決於寫者之間使用的同步機制),讀者沒有任何同步開銷,而寫者的同步開銷則取決於使用的寫者間同步機制。但RCU不能替代讀寫鎖,因為如果寫比較多時,對讀者的效能提高不能彌補寫者導致的損失。

順序鎖也是對讀寫鎖的一種優化,對於順序鎖,讀者絕不會被寫者阻塞,也就說,讀者可以在寫者對被順序鎖保護的共享資源進行寫操作時仍然可以繼續讀,而不必等待寫者完成寫操作,寫者也不需要等待所有讀者完成讀操作才去進行寫操作。但是,寫者與寫者之間仍然是互斥的,即如果有寫者在進行寫操作,其他寫者必須自旋在那裡,直到寫者釋放了順序鎖。

這種鎖有一個限制,它必須要求被保護的共享資源不含有指標,因為寫者可能使得指標失效,但讀者如果正要訪問該指標,將導致OOPs。

如果讀者在讀操作期間,寫者已經發生了寫操作,那麼,讀者必須重新讀取資料,以便確保得到的資料是完整的。

這種鎖對於讀寫同時進行的概率比較小的情況,效能是非常好的,而且它允許讀寫同時進行,因而更大地提高了併發性。