什麼是自旋鎖

多線程中,對共享資源進行訪問,爲了防止併發引起的相關問題,通常都是引入鎖的機制來處理併發問題。

獲取到資源的線程A對這個資源加鎖,其他線程比如B要訪問這個資源首先要獲得鎖,而此時A持有這個資源的鎖,只有等待線程A邏輯執行完,釋放鎖,這個時候B才能獲取到資源的鎖進而獲取到該資源。

這個過程中,A一直持有着資源的鎖,那麼沒有獲取到鎖的其他線程比如B怎麼辦?通常就會有兩種方式:

1. 一種是沒有獲得鎖的進程就直接進入阻塞(BLOCKING),這種就是互斥鎖

2. 另外一種就是沒有獲得鎖的進程,不進入阻塞,而是一直循環着,看是否能夠等到A釋放了資源的鎖。

上述的兩種方式,學術上,就有幾種不同的定義方式,大學的時候 學習的是C++, 《C++ 11》中就有這樣的描述:

自旋鎖(spin lock)是一種非阻塞鎖,也就是說,如果某線程需要獲取鎖,但該鎖已經被其他線程佔用時,該線程不會被掛起,而是在不斷的消耗CPU的時間,不停的試圖獲取鎖。

互斥量(mutex)是阻塞鎖,當某線程無法獲取鎖時,該線程會被直接掛起,該線程不再消耗CPU時間,當其他線程釋放鎖後,操作系統會激活那個被掛起的線程,讓其投入運行。

而《linux內核設計與實現》經常提到兩種態,一種是內核態,一種是用戶態,對於自旋鎖來說,自旋鎖使線程處於用戶態,而互斥鎖需要重新分配,進入到內核態。這裏大家對內核態和用戶態有個初步的認知就行了,用戶態比較輕,內核態比較重。用戶態和內核態這個也是linux中必備的知識基礎,借鑑這個,可以進行很多程序設計語言API上的優化,就比如說javaio的部分,操作io的時候,先是要從用戶態,進入內核態,再用內核態去操作輸入輸出設備的抽象,這裏減少用戶態到內核態的轉換就是新io的一部分優化,後面再聊。

wiki中的定義如下:

自旋鎖是計算機科學用於多線程同步的一種鎖,線程反覆檢查鎖變量是否可用。由於線程在這一過程中保持執行,因此是一種忙等待。

自旋鎖避免了進程上下文的調度開銷,因此對於線程只會阻塞很短時間的場合是有效的。因此操作系統的實現在很多地方往往用自旋鎖。Windows操作系統提供的輕型讀寫鎖(SRW Lock)內部就用了自旋鎖。顯然,單核CPU不適於使用自旋鎖,這裏的單核CPU指的是單核單線程的CPU,因爲,在同一時間只有一個線程是處在運行狀態,假設運行線程A發現無法獲取鎖,只能等待解鎖,但因爲A自身不掛起,所以那個持有鎖的線程B沒有辦法進入運行狀態,只能等到操作系統分給A的時間片用完,纔能有機會被調度。這種情況下使用自旋鎖的代價很高。(紅字部分是我給wiki編輯的詞條,單核CPU不適合自旋鎖,這個也只是針對單核單線程的情況,現在的技術基本單核都是支持多線程的)

爲什麼要使用自旋鎖
互斥鎖有一個缺點,他的執行流程是這樣的 託管代碼  - 用戶態代碼 - 內核態代碼、上下文切換開銷與損耗,假如獲取到資源鎖的線程A立馬處理完邏輯釋放掉資源鎖,如果是採取互斥的方式,那麼線程B從沒有獲取鎖到獲取鎖這個過程中,就要用戶態和內核態調度、上下文切換的開銷和損耗。所以就有了自旋鎖的模式,讓線程B就在用戶態循環等着,減少消耗。

自旋鎖比較適用於鎖使用者保持鎖時間比較短的情況,這種情況下自旋鎖的效率要遠高於互斥鎖。

自旋鎖可能潛在的問題
過多佔用CPU的資源,如果鎖持有者線程A一直長時間的持有鎖處理自己的邏輯,那麼這個線程B就會一直循環等待過度佔用cpu資源
遞歸使用可能會造成死鎖,不過這種場景一般寫不出來。

 

總結:自旋的意思就是while()循環這樣的等待,這樣是會一直消耗cpu的,不會造成任務切換,而且只有多核才能用,單核不是真正的並行,所以不掛起是不行的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章