使用場景: 如果你能確定被鎖住的**執行時間很長,就不應該用互斥鎖
加鎖的目的就是保證共享資源在任意時間裡,只有乙個執行緒訪問,這樣就可以避免多執行緒導致共享資料錯亂的問題。
互斥鎖加鎖失敗後,執行緒會釋放 cpu ,給其他執行緒,自身處於獲取鎖阻塞狀態,然後從使用者態切換到核心態由由核心幫助進行切換執行緒,當加鎖失敗時,核心會將執行緒置為「睡眠」狀態,等到鎖被釋放後,核心會在合適的時機喚醒執行緒,當這個執行緒成功獲取到鎖後,於是就可以繼續執行,著之間的過程會產生上下文切換。
上下問切換的過程,虛擬記憶體是共享的,需要儲存執行緒的私有資料,暫存器等不共享的資料。
使用場景: ,如果你能確定被鎖住的**執行時間很短,就使用自旋鎖
自旋鎖是通過cpu函式在使用者態完成加鎖和解鎖操作,不產生上下文切換, 但是自旋鎖會產生忙等待,自旋的執行緒會占用消耗cpu資源。此種加鎖方式只適合於分時系統,不能工作在單cpu的硬體
讀寫鎖適用於能明確區分讀操作和寫操作的場景
公平讀寫鎖: 比較簡單的一種方式是:用佇列把獲取鎖的執行緒排隊,不管是寫執行緒還是讀執行緒都按照先進先出的原則加鎖即可,這樣讀執行緒仍然可以併發,也不會出現「飢餓」的現象。
前面提到的互斥鎖、自旋鎖、讀寫鎖,都是屬於悲觀鎖。
悲觀鎖做事比較悲觀,它認為多執行緒同時修改共享資源的概率比較高,於是很容易出現衝突,所以訪問共享資源前,先要上鎖。
樂觀鎖做事比較樂觀,它假定衝突的概率很低,它的工作方式是:先修改完共享資源,再驗證這段時間內有沒有發生衝突,如果沒有其他執行緒在修改資源,那麼操作完成,如果發現有其他執行緒已經修改過這個資源,就放棄本次操作。
開發過程中,最常見的就是互斥鎖的了,互斥鎖加鎖失敗時,會用「執行緒切換」來應對,當加鎖失敗的執行緒再次加鎖成功後的這一過程,會有兩次執行緒上下文切換的成本,效能損耗比較大。
如果我們明確知道被鎖住的**的執行時間很短,那我們應該選擇開銷比較小的自旋鎖,因為自旋鎖加鎖失敗時,並不會主動產生執行緒切換,而是一直忙等待,直到獲取到鎖,那麼如果被鎖住的**執行時間很短,那這個忙等待的時間相對應也很短。
如果能區分讀操作和寫操作的場景,那讀寫鎖就更合適了,它允許多個讀執行緒可以同時持有讀鎖,提高了讀的併發性。根據偏袒讀方還是寫方,可以分為讀優先鎖和寫優先鎖,讀優先鎖併發性很強,但是寫執行緒會被餓死,而寫優先鎖會優先服務寫執行緒,讀執行緒也可能會被餓死,那為了避免飢餓的問題,於是就有了公平讀寫鎖,它是用佇列把請求鎖的執行緒排隊,並保證先入先出的原則來對執行緒加鎖,這樣便保證了某種執行緒不會被餓死,通用性也更好點。
另外,互斥鎖、自旋鎖、讀寫鎖都屬於悲觀鎖,悲觀鎖認為併發訪問共享資源時,衝突概率可能非常高,所以在訪問共享資源前,都需要先加鎖。
相反的,如果併發訪問共享資源時,衝突概率非常低的話,就可以使用樂觀鎖,它的工作方式是,在訪問共享資源時,不用先加鎖,修改完共享資源後,再驗證這段時間內有沒有發生衝突,如果沒有其他執行緒在修改資源,那麼操作完成,如果發現有其他執行緒已經修改過這個資源,就放棄本次操作。
互斥鎖 自旋鎖 讀寫鎖 悲觀鎖 樂觀鎖
最底層的兩種就是會 互斥鎖和自旋鎖 有很多高階的鎖都是基於它們實現的,你可以認為它們是各種鎖的地基,所以我們必須清楚它倆之間的區別和應用。加鎖的目的就是保證共享資源在任意時間裡,只有乙個執行緒訪問,這樣就可以避免多執行緒導致共享資料錯亂的問題。當已經有乙個執行緒加鎖後,其他執行緒加鎖則就會失敗,互斥...
Linux 互斥鎖 遞迴鎖 自旋鎖 讀寫鎖
在多執行緒中,我們經常會要用到鎖,那麼,鎖是什麼,我們為什麼要用到鎖?回到問題的本質,我們在什麼場景下會用到鎖?鎖是針對程式中的臨界資源,也就是公共資源的,當我們有兩個或多個執行緒同時對乙個臨界資源操作的時候,為了保證共享資料操作的完整性,我們要為這些公共資源加鎖。在linux中常見的鎖主要有互斥鎖...
互斥鎖 自旋鎖 讀寫鎖 條件變數
互斥鎖 同一時刻只能有乙個執行緒進入臨界區,乙個執行緒獲取鎖如果失敗,則該執行緒進入睡眠狀態,同一執行緒多次加鎖會造成死鎖。使用場景 1.持鎖時間長 2臨界區競爭非常激烈 3 單核處理器 自旋鎖 不會造成執行緒進入睡眠狀態,執行緒會不斷檢測鎖是否已經釋放,減少了執行緒從睡眠到喚醒的核心開銷。使用場景...