欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言底層原理互斥鎖的實現(xiàn)原理

 更新時間:2022年08月10日 08:40:44   作者:阿甘與阿Q  
這篇文章主要介紹了Go語言底層原理互斥鎖的實現(xiàn)原理,Go?sync包提供了兩種鎖類型,分別是互斥鎖sync.Mutex和讀寫互斥鎖sync.RWMutex,都屬于悲觀鎖,更多相關(guān)內(nèi)容需要的朋友可以查看下面文章內(nèi)容

Go 互斥鎖的實現(xiàn)原理?

Go sync包提供了兩種鎖類型:互斥鎖sync.Mutex 和 讀寫互斥鎖sync.RWMutex,都屬于悲觀鎖。

概念

Mutex是互斥鎖,當一個 goroutine 獲得了鎖后,其他 goroutine 不能獲取鎖(只能存在一個寫者或讀者,不能同時讀和寫)

使用場景

多個線程同時訪問臨界區(qū),為保證數(shù)據(jù)的安全,鎖住一些共享資源, 以防止并發(fā)訪問這些共享數(shù)據(jù)時可能導(dǎo)致的數(shù)據(jù)不一致問題。

獲取鎖的線程可以正常訪問臨界區(qū),未獲取到鎖的線程等待鎖釋放后可以嘗試獲取鎖

底層實現(xiàn)結(jié)構(gòu)

互斥鎖對應(yīng)的是底層結(jié)構(gòu)是sync.Mutex結(jié)構(gòu)體,,位于 src/sync/mutex.go中

type Mutex struct {
     state int32
     sema  uint32
 }

state表示鎖的狀態(tài),有鎖定、被喚醒、饑餓模式等,并且是用state的二進制位來標識的,不同模式下會有不同的處理方式

sema表示信號量,mutex阻塞隊列的定位是通過這個變量來實現(xiàn)的,從而實現(xiàn)goroutine的阻塞和喚醒

addr = &sema
func semroot(addr *uint32) *semaRoot {
   return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root  
}
root := semroot(addr)
root.queue(addr, s, lifo)
root.dequeue(addr)

var semtable [251]struct {
   root semaRoot
   ...
}

type semaRoot struct {
  lock  mutex  
  treap *sudog // root of balanced tree of unique waiters.  
  nwait uint32 // Number of waiters. Read w/o the lock.  
}

type sudog struct {
    g *g  
    next *sudog  
    prev *sudog
    elem unsafe.Pointer // 指向sema變量
    waitlink *sudog // g.waiting list or semaRoot  
    waittail *sudog // semaRoot
    ...
}

操作

鎖的實現(xiàn)一般會依賴于原子操作、信號量,通過atomic 包中的一些原子操作來實現(xiàn)鎖的鎖定,通過信號量來實現(xiàn)線程的阻塞與喚醒

加鎖

通過原子操作cas加鎖,如果加鎖不成功,根據(jù)不同的場景選擇自旋重試加鎖或者阻塞等待被喚醒后加鎖

func (m *Mutex) Lock() {
    // Fast path: 幸運之路,一下就獲取到了鎖
    if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
        return
    }
    // Slow path:緩慢之路,嘗試自旋或阻塞獲取鎖
    m.lockSlow()
}

解鎖

通過原子操作add解鎖,如果仍有g(shù)oroutine在等待,喚醒等待的goroutine

func (m *Mutex) Unlock() {  
   // Fast path: 幸運之路,解鎖
   new := atomic.AddInt32(&m.state, -mutexLocked)  
   if new != 0 {  
            // Slow path:如果有等待的goroutine,喚醒等待的goroutine
            m.unlockSlow()
   }  
}

注意點:

  • 在 Lock() 之前使用 Unlock() 會導(dǎo)致 panic 異常
  • 使用 Lock() 加鎖后,再次 Lock() 會導(dǎo)致死鎖(不支持重入),需Unlock()解鎖后才能再加鎖
  • 鎖定狀態(tài)與 goroutine 沒有關(guān)聯(lián),一個 goroutine 可以 Lock,另一個 goroutine 可以 Unlock

Go 互斥鎖正常模式和饑餓模式的區(qū)別?

在Go一共可以分為兩種搶鎖的模式,一種是正常模式,另外一種是饑餓模式。

正常模式(非公平鎖)

在剛開始的時候,是處于正常模式(Barging),也就是,當一個G1持有著一個鎖的時候,G2會自旋的去嘗試獲取這個鎖

自旋超過4次還沒有能獲取到鎖的時候,這個G2就會被加入到獲取鎖的等待隊列里面,并阻塞等待喚醒

正常模式下,所有等待鎖的 goroutine 按照 FIFO(先進先出)順序等待。喚醒的goroutine 不會直接擁有鎖,而是會和新請求鎖的 goroutine 競爭鎖。新請求鎖的 goroutine 具有優(yōu)勢:它正在 CPU 上執(zhí)行,而且可能有好幾個,所以剛剛喚醒的 goroutine 有很大可能在鎖競爭中失敗,長時間獲取不到鎖,就會切換到饑餓模式

饑餓模式(公平鎖)

當一個 goroutine 等待鎖時間超過 1 毫秒時,它可能會遇到饑餓問題。 在版本1.9中,這種場景下Go Mutex 切換到饑餓模式(handoff),解決饑餓問題。

starving = runtime_nanotime()-waitStartTime > 1e6

正常模式下,所有等待鎖的 goroutine 按照 FIFO(先進先出)順序等待。喚醒的goroutine 不會直接擁有鎖,而是會和新請求鎖的 goroutine 競爭鎖。新請求鎖的 goroutine 具有優(yōu)勢:它正在 CPU 上執(zhí)行,而且可能有好幾個,所以剛剛喚醒的 goroutine 有很大可能在鎖競爭中失敗,長時間獲取不到鎖,就會切換到饑餓模式

那么也不可能說永遠的保持一個饑餓的狀態(tài),總歸會有吃飽的時候,也就是總有那么一刻Mutex會回歸到正常模式,那么回歸正常模式必須具備的條件有以下幾種:

  • G的執(zhí)行時間小于1ms
  • 等待隊列已經(jīng)全部清空了

當滿足上述兩個條件的任意一個的時候,Mutex會切換回正常模式,而Go的搶鎖的過程,就是在這個正常模式和饑餓模式中來回切換進行的。

delta := int32(mutexLocked - 1<<mutexWaiterShift)  
if !starving || old>>mutexWaiterShift == 1 {  
    delta -= mutexStarving
}
atomic.AddInt32(&m.state, delta)

小結(jié):

對于兩種模式,正常模式下的性能是最好的,goroutine 可以連續(xù)多次獲取鎖,饑餓模式解決了取鎖公平的問題,但是性能會下降,其實是性能和公平的 一個平衡模式。

Go 互斥鎖允許自旋的條件?

線程沒有獲取到鎖時常見有2種處理方式:

  • 一種是沒有獲取到鎖的線程就一直循環(huán)等待判斷該資源是否已經(jīng)釋放鎖,這種鎖也叫做自旋鎖,它不用將線程阻塞起來, 適用于并發(fā)低且程序執(zhí)行時間短的場景,缺點是cpu占用較高
  • 另外一種處理方式就是把自己阻塞起來,會釋放CPU給其他線程,內(nèi)核會將線程置為「睡眠」狀態(tài),等到鎖被釋放后,內(nèi)核會在合適的時機喚醒該線程,適用于高并發(fā)場景,缺點是有線程上下文切換的開銷

Go語言中的Mutex實現(xiàn)了自旋與阻塞兩種場景,當滿足不了自旋條件時,就會進入阻塞

允許自旋的條件:

  • 鎖已被占用,并且鎖不處于饑餓模式。
  • 積累的自旋次數(shù)小于最大自旋次數(shù)(active_spin=4)。
  • cpu 核數(shù)大于 1。
  • 有空閑的 P。
  • 當前 goroutine 所掛載的 P 下,本地待運行隊列為空。
if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {  
    ...
    runtime_doSpin()   
    continue  
}


func sync_runtime_canSpin(i int) bool {  
    if i >= active_spin 
    || ncpu <= 1 
    || gomaxprocs <= int32(sched.npidle+sched.nmspinning)+1 {  
          return false  
     }  
   if p := getg().m.p.ptr(); !runqempty(p) {  
      return false  
 }  
   return true  
}

自旋:

func sync_runtime_doSpin() {
    procyield(active_spin_cnt)
}    

如果可以進入自旋狀態(tài)之后就會調(diào)用 runtime_doSpin 方法進入自旋, doSpin 方法會調(diào)用 procyield(30) 執(zhí)行30次 PAUSE 指令,什么都不做,但是會消耗CPU時間

到此這篇關(guān)于Go語言底層原理互斥鎖的實現(xiàn)原理的文章就介紹到這了,更多相關(guān)Go 互斥鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用golang實現(xiàn)一個MapReduce的示例代碼

    使用golang實現(xiàn)一個MapReduce的示例代碼

    這篇文章主要給大家介紹了關(guān)于如何使用golang實現(xiàn)一個MapReduce,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-09-09
  • 使用client go實現(xiàn)自定義控制器的方法

    使用client go實現(xiàn)自定義控制器的方法

    本文我們來使用client-go實現(xiàn)一個自定義控制器,通過判斷service的Annotations屬性是否包含ingress/http,如果包含則創(chuàng)建ingress,如果不包含則不創(chuàng)建,對client go自定義控制器相關(guān)知識感興趣的朋友一起看看吧
    2022-05-05
  • 淺談golang slice 切片原理

    淺談golang slice 切片原理

    這篇文章主要介紹了淺談golang slice 切片原理,詳細的介紹了golang slice 切片的概念和原理,具有一定的參考價值,有興趣的可以了解一下
    2017-11-11
  • golang1.21泛型函數(shù)全面講解

    golang1.21泛型函數(shù)全面講解

    在Go編程語言中,泛型一直是一個備受期待的特性,隨著Go?1.21的發(fā)布,本文旨在提供Go?1.21中泛型的詳細探索,闡明它們的優(yōu)點、語法、實現(xiàn)和最佳實踐,希望對大家有所幫助
    2023-09-09
  • golang實現(xiàn)簡單工廠、方法工廠、抽象工廠三種設(shè)計模式

    golang實現(xiàn)簡單工廠、方法工廠、抽象工廠三種設(shè)計模式

    這篇文章介紹了golang實現(xiàn)簡單工廠、方法工廠、抽象工廠三種設(shè)計模式的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • Go?gRPC服務(wù)進階middleware使用教程

    Go?gRPC服務(wù)進階middleware使用教程

    這篇文章主要為大家介紹了Go?gRPC服務(wù)進階middleware的使用教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • go語言按顯示長度截取字符串的方法

    go語言按顯示長度截取字符串的方法

    這篇文章主要介紹了go語言按顯示長度截取字符串的方法,涉及Go語言操作字符串的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • 詳解Golang中的通道機制與應(yīng)用

    詳解Golang中的通道機制與應(yīng)用

    這篇文章主要來和大家一起深入探討了Go語言中通道(Channel)的各個方面,文章詳細解析了通道的類型、操作方法以及垃圾回收機制,有需要的可以了解下
    2023-10-10
  • Go語言指針用法詳解

    Go語言指針用法詳解

    Go指針和C指針在許多方面非常相似,但其中也有一些不同。本文詳細講解了Go語言指針的用法,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • win7下配置GO語言環(huán)境 + eclipse配置GO開發(fā)

    win7下配置GO語言環(huán)境 + eclipse配置GO開發(fā)

    這篇文章主要介紹了win7下配置GO語言環(huán)境 + eclipse配置GO開發(fā),需要的朋友可以參考下
    2014-10-10

最新評論