亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Golang自旋鎖怎么實現

發布時間:2022-10-12 09:16:04 來源:億速云 閱讀:128 作者:iii 欄目:開發技術

這篇文章主要介紹了Golang自旋鎖怎么實現的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Golang自旋鎖怎么實現文章都會有所收獲,下面我們一起來看看吧。

    自旋鎖

    獲取鎖的線程一直處于活躍狀態,但是并沒有執行任何有效的任務,使用這種鎖會造成busy-waiting。 它是為實現保護共享資源而提出的一種鎖機制。其實,自旋鎖與互斥鎖比較類似,它們都是為了解決某項資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能由一個保持者,也就說,在任何時刻最多只能有一個執行單元獲得鎖。但是兩者在調度機制上略有不同。對于互斥鎖,如果資源已經被占用,資源申請者只能進入睡眠狀態。但是自旋鎖不會引起調用者睡眠,如果自旋鎖已經被別的執行單元保持,調用者就一直循環在那里看是否該自旋鎖的保持者已經釋放了鎖,“自旋”一詞就是因此而得名。

    golang實現自旋鎖

    type spinLock uint32
    func (sl *spinLock) Lock() {
        for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
            runtime.Gosched()
        }
    }
    func (sl *spinLock) Unlock() {
        atomic.StoreUint32((*uint32)(sl), 0)
    }
    func NewSpinLock() sync.Locker {
        var lock spinLock
        return &lock
    }

    可重入的自旋鎖和不可重入的自旋鎖

    上面的代碼,仔細分析一下就可以看出,它是不支持重入的,即當一個線程第一次已經獲取到了該鎖,在鎖釋放之前又一次重新獲取該鎖,第二次就不能成功獲取到。由于不滿足CAS,所以第二次獲取會進入while循環等待,而如果是可重入鎖,第二次也是應該能夠成功獲取到的。

    而且,即使第二次能夠成功獲取,那么當第一次釋放鎖的時候,第二次獲取到的鎖也會被釋放,而這是不合理的。

    為了實現可重入鎖,我們需要引入一個計數器,用來記錄獲取鎖的線程數

    type spinLock struct {
          owner int
          count  int
    }
    func (sl *spinLock) Lock() {
            me := GetGoroutineId()
            if spinLock .owner == me { // 如果當前線程已經獲取到了鎖,線程數增加一,然后返回
                   sl.count++
                   return
            }
            // 如果沒獲取到鎖,則通過CAS自旋
        for !atomic.CompareAndSwapUint32((*uint32)(sl), 0, 1) {
            runtime.Gosched()
        }
    }
    func (sl *spinLock) Unlock() {
          if  rl.owner != GetGoroutineId() {
              panic("illegalMonitorStateError")
          }
          if sl.count >0  { // 如果大于0,表示當前線程多次獲取了該鎖,釋放鎖通過count減一來模擬
               sl.count--
           }else { // 如果count==0,可以將鎖釋放,這樣就能保證獲取鎖的次數與釋放鎖的次數是一致的了。
               atomic.StoreUint32((*uint32)(sl), 0)
           }
    }
    func GetGoroutineId() int {
        defer func()  {
            if err := recover(); err != nil {
                fmt.Println("panic recover:panic info:%v", err)     }
        }()
        var buf [64]byte
        n := runtime.Stack(buf[:], false)
        idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
        id, err := strconv.Atoi(idField)
        if err != nil {
            panic(fmt.Sprintf("cannot get goroutine id: %v", err))
        }
        return id
    }
    func NewSpinLock() sync.Locker {
        var lock spinLock
        return &lock
    }

    自旋鎖的其他變種

    1. TicketLock

    TicketLock主要解決的是公平性的問題。

    思路:每當有線程獲取鎖的時候,就給該線程分配一個遞增的id,我們稱之為排隊號,同時,鎖對應一個服務號,每當有線程釋放鎖,服務號就會遞增,此時如果服務號與某個線程排隊號一致,那么該線程就獲得鎖,由于排隊號是遞增的,所以就保證了最先請求獲取鎖的線程可以最先獲取到鎖,就實現了公平性。

    可以想象成銀行辦業務排隊,排隊的每一個顧客都代表一個需要請求鎖的線程,而銀行服務窗口表示鎖,每當有窗口服務完成就把自己的服務號加一,此時在排隊的所有顧客中,只有自己的排隊號與服務號一致的才可以得到服務。

    2. CLHLock

    CLH鎖是一種基于鏈表的可擴展、高性能、公平的自旋鎖,申請線程只在本地變量上自旋,它不斷輪詢前驅的狀態,如果發現前驅釋放了鎖就結束自旋,獲得鎖。

    3. MCSLock

    MCSLock則是對本地變量的節點進行循環。

    4. CLHLock 和 MCSLock

    都是基于鏈表,不同的是CLHLock是基于隱式鏈表,沒有真正的后續節點屬性,MCSLock是顯示鏈表,有一個指向后續節點的屬性。

    將獲取鎖的線程狀態借助節點(node)保存,每個線程都有一份獨立的節點,這樣就解決了TicketLock多處理器緩存同步的問題。

    自旋鎖與互斥鎖

    • 自旋鎖與互斥鎖都是為了實現保護資源共享的機制。

    • 無論是自旋鎖還是互斥鎖,在任意時刻,都最多只能有一個保持者。

    • 獲取互斥鎖的線程,如果鎖已經被占用,則該線程將進入睡眠狀態;獲取自旋鎖的線程則不會睡眠,而是一直循環等待鎖釋放。

    關于“Golang自旋鎖怎么實現”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Golang自旋鎖怎么實現”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    五台县| 梨树县| 雷山县| 大田县| 玛曲县| 陈巴尔虎旗| 香格里拉县| 景谷| 丹寨县| 内乡县| 镇平县| 界首市| 伊春市| 上蔡县| 牡丹江市| 安宁市| 舟山市| 甘孜县| 徐汇区| 焦作市| 台东县| 侯马市| 闵行区| 南召县| 大渡口区| 游戏| 玉门市| 临夏县| 高州市| 麻城市| 金乡县| 保靖县| 孟州市| 深泽县| 英超| 望谟县| 福安市| 邳州市| 乌鲁木齐县| 海原县| 桓台县|