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

Golang?鎖原理的簡單實現(xiàn)

 更新時間:2023年03月15日 10:51:19   作者:獨臂阿童木  
本文主要介紹了Golang?鎖原理的簡單實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

什么是鎖

  • 鎖的本質(zhì),就是一種資源,是由操作系統(tǒng)維護(hù)的一種專門用于同步的資源
  • 比如說互斥鎖,說白了就是一種互斥的資源。只能有一個進(jìn)程(線程)占有。當(dāng)一個進(jìn)程(線程)通過競爭獲得鎖的時候,其他進(jìn)程(或線程)將得不到這把鎖。這是內(nèi)核代碼決定的
  • 如果我們希望某種資源在多個進(jìn)程(線程/協(xié)程)之間共享,但是某一時刻最多有一個進(jìn)程占有,這不就是互斥鎖的概念嗎,也就是說,我們希望自己的資源也變成一種鎖
  • 最簡單的辦法就是將自己的資源和操作系統(tǒng)定義好的鎖綁定到一起。也就是說,進(jìn)程要獲取我的資源之前,必須要獲得操作系統(tǒng)的鎖。進(jìn)一步說,得鎖得資源,失鎖失資源。這樣的話,我們的資源也變成了一把鎖

為什么使用鎖

并發(fā)編程中保證數(shù)據(jù)一致性和安全性的

Golang中的鎖

Golang的提供的同步機(jī)制有sync模塊下的Mutex、WaitGroup以及語言自身提供的chan等。 這些同步的方法都是以runtime中實現(xiàn)的底層同步機(jī)制(cas、atomic、spinlock、sem)為基礎(chǔ)的

1. cas、atomic

cas(Compare And Swap)和原子運算是其他同步機(jī)制的基礎(chǔ)

  • 原子操作:指那些不能夠被打斷的操作被稱為原子操作,當(dāng)有一個CPU在訪問這塊內(nèi)容addr時,其他CPU就不能訪問
  • CAS:比較及交換,其實也屬于原子操作,但它是非阻塞的,所以在被操作值被頻繁變更的情況下,CAS操作并不那么容易成功,不得不利用for循環(huán)以進(jìn)行多次嘗試

2. 自旋鎖(spinlock)

自旋鎖是指當(dāng)一個線程在獲取鎖的時候,如果鎖已經(jīng)被其他線程獲取,那么該線程將循環(huán)等待,然后不斷地判斷是否能夠被成功獲取,知直到獲取到鎖才會退出循環(huán)。獲取鎖的線程一直處于活躍狀態(tài)
Golang中的自旋鎖用來實現(xiàn)其他類型的鎖,與互斥鎖類似,不同點在于,它不是通過休眠來使進(jìn)程阻塞,而是在獲得鎖之前一直處于活躍狀態(tài)(自旋)

3. 信號量

實現(xiàn)休眠和喚醒協(xié)程的一種方式

信號量有兩個操作P和V
P(S):分配一個資源
1. 資源數(shù)減1:S=S-1
2. 進(jìn)行以下判斷
    如果S<0,進(jìn)入阻塞隊列等待被釋放
    如果S>=0,直接返回

V(S):釋放一個資源
1. 資源數(shù)加1:S=S+1
2. 進(jìn)行如下判斷
    如果S>0,直接返回
    如果S<=0,表示還有進(jìn)程在請求資源,釋放阻塞隊列中的第一個等待進(jìn)程
    
golang中信號量操作:runtime/sema.go
P操作:runtime_Semacquire
V操作:runtime_Semrelease

mutex的使用

package main

import (
? ? "fmt"
? ? "sync"
)

var num int
var mtx sync.Mutex
var wg sync.WaitGroup

func add() {
? ? mtx.Lock() ?//mutex實例無需實例化,聲明即可使用

? ? defer mtx.Unlock()
? ? defer wg.Done()

? ? num += 1
}

func main() {
? ? for i := 0; i < 100; i++ {
? ? ? ? wg.Add(1)
? ? ? ? go add()
? ? }

? ? wg.Wait()

? ? fmt.Println("num:", num)
}

mutex的必要性

鎖在高度競爭時會不斷掛起恢復(fù)線程從而讓出cpu資源,原子變量在高度競爭時會一直占用cpu;原子操作時線程級別的,不支持協(xié)程

mutex演進(jìn)

1. 互斥鎖

type Mutex struct {
? ? state int32
? ? sema ?uint32
}

const (
? ? mutexLocked = 1 << iota
? ? mutexWoken
? ? mutexWaiterShift = iota ?//根據(jù) mutex.state >> mutexWaiterShift 得到當(dāng)前等待的 goroutine 數(shù)目
)

state表示當(dāng)前鎖的狀態(tài),是一個共用變量

state:  |32|31|....|3|2|1|
         \__________/ | |
               |      | |
               |      | 當(dāng)前mutex是否加鎖
               |      |
               |      當(dāng)前mutex是否被喚醒
               |
               等待隊列的goroutine協(xié)程數(shù)

Lock 方法申請對 mutex 加鎖的時候分兩種情況

  • 無沖突 通過 CAS 操作把當(dāng)前狀態(tài)設(shè)置為加鎖狀態(tài)
  • 有沖突 通過調(diào)用 semacquire 函數(shù)來讓當(dāng)前 goroutine 進(jìn)入休眠狀態(tài),等待其他協(xié)程釋放鎖的時候喚醒
//如果已經(jīng)加鎖,那么當(dāng)前協(xié)程進(jìn)入休眠阻塞,等待喚醒
func (m *Mutex) Lock() {

? ? // 快速加鎖:CAS更新state為locked
? ? if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
? ? ? ? return
? ? }

? ? awoke := false //當(dāng)前goroutine是否被喚醒
? ? for {
? ? ? ? old := m.state // 保存當(dāng)前state的狀態(tài)
? ? ? ? new := old | mutexLocked // 新值locked位設(shè)置為1
? ? ? ? // 如果當(dāng)前處于加鎖狀態(tài),新到來的goroutine進(jìn)入等待隊列
? ? ? ? if old&mutexLocked != 0 {
? ? ? ? ? ? new = old + 1<<mutexWaiterShift
? ? ? ? }
? ? ? ? if awoke {
? ? ? ? ? ? //如果被喚醒,新值需要重置woken位為 0
? ? ? ? ? ? new &^= mutexWoken
? ? ? ? }
? ? ? ??
? ? ? ? // 兩種情況會走到這里:1.休眠中被喚醒 2.加鎖失敗進(jìn)入等待隊列
? ? ? ? // CAS 更新,如果更新失敗,說明有別的協(xié)程搶先一步,那么重新發(fā)起競爭。
? ? ? ? if atomic.CompareAndSwapInt32(&m.state, old, new) {
? ? ? ? ? ? // 如果更新成功,有兩種情況
? ? ? ? ? ? // 1.如果為 1,說明當(dāng)前 CAS 是為了更新 waiter 計數(shù)
? ? ? ? ? ? // 2.如果為 0,說明是搶鎖成功,那么直接 break 退出。
? ? ? ? ? ? if old&mutexLocked == 0 {?
? ? ? ? ? ? ? ? break
? ? ? ? ? ? }
? ? ? ? ? ? runtime_Semacquire(&m.sema) // 此時如果 sema <= 0 那么阻塞在這里等待喚醒,也就是 park 住。走到這里都是要休眠了。
? ? ? ? ? ? awoke = true ?// 有人釋放了鎖,然后當(dāng)前 goroutine 被 runtime 喚醒了,設(shè)置 awoke true
? ? ? ? }
? ? }

? ? if raceenabled {
? ? ? ? raceAcquire(unsafe.Pointer(m))
? ? }
}

UnLock 解鎖分兩步

  • 解鎖,通過CAS操作把當(dāng)前狀態(tài)設(shè)置為解鎖狀態(tài)
  • 喚醒休眠協(xié)程,CAS操作把當(dāng)前狀態(tài)的waiter數(shù)減1,然后喚醒休眠goroutine
//鎖沒有和某個特定的協(xié)程關(guān)聯(lián),可以由一個協(xié)程lock,另一個協(xié)程unlock
func (m *Mutex) Unlock() {
? ? if raceenabled {
? ? ? ? _ = m.state
? ? ? ? raceRelease(unsafe.Pointer(m))
? ? }

? ? // CAS更新state的狀態(tài)為locked 注意:解鎖的瞬間可能會有新的協(xié)程到來并搶到鎖
? ? new := atomic.AddInt32(&m.state, -mutexLocked)
? ? // 釋放了一個沒上鎖的鎖會panic:原先的lock位為0
? ? if (new+mutexLocked)&mutexLocked == 0 {?
? ? ? ? panic("sync: unlock of unlocked mutex")
? ? }
? ??
? ? //判斷是否需要釋放資源
? ? old := new
? ? for {
? ? ? ? /**
? ? ? ? ?* 不需要喚醒的情況
? ? ? ? ?* 1.等待隊列為0
? ? ? ? ?* 2.已經(jīng)有協(xié)程搶到鎖(上面的瞬間搶鎖)
? ? ? ? ?* 3.已經(jīng)有協(xié)程被喚醒
? ? ? ? ?*/
? ? ? ? if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
? ? ? ? ? ? return
? ? ? ? }
? ? ? ? //將waiter計數(shù)位減一,并設(shè)置state為woken(喚醒)
? ? ? ? //問:會同時有多個被喚醒的協(xié)程存在嗎
? ? ? ? new = (old - 1<<mutexWaiterShift) | mutexWoken
? ? ? ? if atomic.CompareAndSwapInt32(&m.state, old, new) {
? ? ? ? ? ? runtime_Semrelease(&m.sema) // cas成功后,再做sema release操作,喚醒休眠的 goroutine
? ? ? ? ? ? return
? ? ? ? }
? ? ? ? old = m.state
? ? }
}

知識點

使用&來判斷位值,使用|來設(shè)置位值,使用&^來清空位置(內(nèi)存對齊)

一代互斥鎖的問題

處于休眠中的goroutine優(yōu)先級低于當(dāng)前活躍的,unlock解鎖的瞬間最新的goroutine會搶到鎖
大多數(shù)果鎖的時間很短,所有的goroutine都要休眠,增加runtime調(diào)度開銷

2. 自旋鎖

Lock 方法申請對 mutex 加鎖的時候分三種情況

  • 無沖突 通過 CAS 操作把當(dāng)前狀態(tài)設(shè)置為加鎖狀態(tài)
  • 有沖突 開始自旋,并等待鎖釋放,如果其他 goroutine 在這段時間內(nèi)釋放了該鎖,直接獲得該鎖;如果沒有釋放,進(jìn)入3
  • 有沖突 通過調(diào)用 semacquire 函數(shù)來讓當(dāng)前 goroutine 進(jìn)入等待狀態(tài),等待其他協(xié)程釋放鎖的時候喚醒
func (m *Mutex) Lock() {
? ? //快速加鎖,邏輯不變
? ? if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
? ? ? ? if race.Enabled {
? ? ? ? ? ? race.Acquire(unsafe.Pointer(m))
? ? ? ? }
? ? ? ? return
? ? }

? ? awoke := false
? ? iter := 0
? ? for {
? ? ? ? old := m.state
? ? ? ? new := old | mutexLocked
? ? ? ? if old&mutexLocked != 0 { // 如果當(dāng)前己經(jīng)上鎖,那么判斷是否可以自旋
? ? ? ? ? ? //短暫的自旋過后如果無果,就只能通過信號量讓當(dāng)前goroutine進(jìn)入休眠等待了
? ? ? ? ? ? if runtime_canSpin(iter) {
? ? ? ? ? ? ? ? // Active spinning makes sense.
? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ? ? ?* 自旋的操作:設(shè)置state為woken,這樣在unlock的時候就不會喚醒其他協(xié)程.
? ? ? ? ? ? ? ? ?* 自旋的條件:
? ? ? ? ? ? ? ? ?* 1.當(dāng)前協(xié)程未被喚醒 !awoke
? ? ? ? ? ? ? ? ?* 2.其他協(xié)程未被喚醒 old&mutexWoken == 0
? ? ? ? ? ? ? ? ?* 3.等待隊列大于0
? ? ? ? ? ? ? ? ?*/
? ? ? ? ? ? ? ? if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
? ? ? ? ? ? ? ? ? ? atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
? ? ? ? ? ? ? ? ? ? awoke = true
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? //進(jìn)行自旋操作
? ? ? ? ? ? ? ? runtime_doSpin()
? ? ? ? ? ? ? ? iter++
? ? ? ? ? ? ? ? continue
? ? ? ? ? ? }
? ? ? ? ? ? new = old + 1<<mutexWaiterShit
? ? ? ? }
? ? ? ? if awoke {
? ? ? ? ? ? //todo 為什么加這個判斷
? ? ? ? ? ? if new&mutexWoken == 0 {
? ? ? ? ? ? ? ? panic("sync: inconsistent mutex state")
? ? ? ? ? ? }
? ? ? ? ? ? new &^= mutexWoken
? ? ? ? }
? ? ? ? if atomic.CompareAndSwapInt32(&m.state, old, new) {
? ? ? ? ? ? if old&mutexLocked == 0 {
? ? ? ? ? ? ? ? break
? ? ? ? ? ? }
? ? ? ? ? ? runtime_Semacquire(&m.sema)
? ? ? ? ? ? awoke = true
? ? ? ? ? ? iter = 0
? ? ? ? }
? ? }

? ? if race.Enabled {
? ? ? ? race.Acquire(unsafe.Pointer(m))
? ? }
}

path: runtime/proc.go

const (
? ? ? ? mutex_unlocked = 0
? ? ? ? mutex_locked ? = 1
? ? ? ? mutex_sleeping = 2

? ? ? ? active_spin ? ? = 4
? ? ? ? active_spin_cnt = 30
? ? ? ? passive_spin ? ?= 1
)

/**
?* 有四種情況會返回false
?* 1.已經(jīng)執(zhí)行了很多次 iter >= active_spin 默認(rèn)為4。避免長時間自旋浪費CPU
?* 2.是單核CPU ncpu <= 1 || GOMAXPROCS < 1 保證除了當(dāng)前運行的Goroutine之外,還有其他的Goroutine在運行
?* 3.沒有其他正在運行的p
?* 4 當(dāng)前P的G隊列為空 避免自旋鎖等待的條件是由當(dāng)前p的其他G來觸發(fā),這樣會導(dǎo)致再自旋變得沒有意義,因為條件永遠(yuǎn)無法觸發(fā)
?*/
func sync_runtime_canSpin(i int) bool {
? ? ? ? // sync.Mutex is cooperative, so we are conservative with spinning.
? ? ? ? // Spin only few times and only if running on a multicore machine and
? ? ? ? // GOMAXPROCS>1 and there is at least one other running P and local runq is empty.
? ? ? ? // As opposed to runtime mutex we don't do passive spinning here,
? ? ? ? // because there can be work on global runq or on other Ps.
? ? ? ? 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
}

// 自旋邏輯
// procyeld函數(shù)內(nèi)部循環(huán)調(diào)用PAUSE指令,PAUSE指令什么都不做,但是會消耗CPU時間
// 在這里會執(zhí)行30次PAUSE指令消耗CPU時間等待鎖的釋放;
func sync_runtime_doSpin() {
? ? procyield(active_spin_cnt)
}

TEXT runtime·procyield(SB),NOSPLIT,$0-0
? ? MOVL ? ?cycles+0(FP), AX
again:
? ? PAUSE
? ? SUBL ? ?$1, AX
? ? JNZ again
? ? RET

問題:

  • 還是沒有解決休眠進(jìn)程優(yōu)先級低的問題

3. 公平鎖

基本邏輯

  • Mutex 兩種工作模式,normal 正常模式,starvation 饑餓模式。normal 情況下鎖的邏輯與老版相似,休眠的 goroutine 以 FIFO 鏈表形式保存在 sudog 中,被喚醒的 goroutine 與新到來活躍的 goroutine 競解,但是很可能會失敗。如果一個 goroutine 等待超過 1ms,那么 Mutex 進(jìn)入饑餓模式
  • 饑餓模式下,解鎖后,鎖直接交給 waiter FIFO 鏈表的第一個,新來的活躍 goroutine 不參與競爭,并放到 FIFO 隊尾
  • 如果當(dāng)前獲得鎖的 goroutine 是 FIFO 隊尾,或是等待時長小于 1ms,那么退出饑餓模式
  • normal 模式下性能是比較好的,但是 starvation 模式能減小長尾 latency

LOCK流程:

  • 無沖突 通過 CAS 操作把當(dāng)前狀態(tài)設(shè)置為加鎖狀態(tài)
  • 有沖突 開始自旋 如果是饑餓模式禁止自旋,開始自旋,并等待鎖釋放,如果其他 goroutine 在這段時間內(nèi)釋放了該鎖,直接獲得該鎖;如果沒有釋放,進(jìn)入3
  • 有沖突,且已經(jīng)過了自旋階段 通過調(diào)用 semacquire 函數(shù)來讓當(dāng)前 goroutine 進(jìn)入等待狀態(tài),等待其他協(xié)程釋放鎖的時候喚醒,休眠前:如果是饑餓模式,把當(dāng)前協(xié)程放到隊列最前面;喚醒后:如果是饑餓模式喚醒的,直接獲得鎖
type Mutex struct {
? ? ? ? state int32?
? ? ? ? sema ?**uint32**
}

// A Locker represents an object that can be locked and unlocked.
type Locker interface {
? ? ? ? Lock()
? ? ? ? Unlock()
}

//為什么使用位掩碼表達(dá)式
//第3位到第32位表示等待在mutex上協(xié)程數(shù)量
const (
? ? ? ? mutexLocked = 1 << iota // mutex is locked?
? ? ? ? mutexWoken ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? mutexStarving ? ? ? ? ? //新增饑餓狀態(tài)
? ? ? ? mutexWaiterShift = iota ? ? ? ? ? ? ? ? ? ??
? ? ? ? starvationThresholdNs = 1e6 //饑餓狀態(tài)的閾值:等待時間超過1ms就會進(jìn)入饑餓狀態(tài)
)


func (m *Mutex) Lock() {
? ? ? ? //快速加鎖:邏輯不變
? ? ? ? if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
? ? ? ? ? ? ? ? if race.Enabled {
? ? ? ? ? ? ? ? ? ? ? ? race.Acquire(unsafe.Pointer(m))
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? return
? ? ? ? }

? ? ? ? var waitStartTime int64 //等待時間
? ? ? ? starving := false ? //饑餓標(biāo)記
? ? ? ? awoke := false ? ? ?//喚醒標(biāo)記
? ? ? ? iter := 0 ? ? ? ? ? //循環(huán)計數(shù)器
? ? ? ? old := m.state ? ? ?//保存當(dāng)前鎖狀態(tài)
? ? ? ? for {
? ? ? ? ? ? // 自旋的時候增加了一個判斷:如果處于饑餓狀態(tài)就不進(jìn)入自旋,因為饑餓模式下,釋放的鎖會直接給等待隊列的第一個,當(dāng)前協(xié)程直接進(jìn)入等待隊列
? ? ? ? ? ? ? ? if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
? ? ? ? ? ? ? ? ? ? ? ? if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? awoke = true
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? runtime_doSpin()
? ? ? ? ? ? ? ? ? ? ? ? iter++
? ? ? ? ? ? ? ? ? ? ? ? old = m.state
? ? ? ? ? ? ? ? ? ? ? ? continue
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? new := old
? ? ? ? ? ? ? ? // 當(dāng)mutex不處于饑餓狀態(tài)的時候,將new值設(shè)置為locked,也就是說如果是饑餓狀態(tài),新到來的goroutine直接排隊
? ? ? ? ? ? ? ? if old&mutexStarving == 0 {
? ? ? ? ? ? ? ? ? ? ? ? new |= mutexLocked
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 當(dāng)mutex處于加鎖鎖或者饑餓狀態(tài)時,新到來的goroutine進(jìn)入等待隊列
? ? ? ? ? ? ? ? if old&(mutexLocked|mutexStarving) != 0 {
? ? ? ? ? ? ? ? ? ? ? ? new += 1 << mutexWaiterShift
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? // 當(dāng)?shù)却龝r間超過閾值,當(dāng)前goroutine切換mutex為饑餓模式,如果未加鎖,就不需要切換
? ? ? ? ? ? ? ? if starving && old&mutexLocked != 0 {
? ? ? ? ? ? ? ? ? ? ? ? new |= mutexStarving
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if awoke {
? ? ? ? ? ? ? ? ? ? ? ? if new&mutexWoken == 0 {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? throw("sync: inconsistent mutex state")
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? new &^= mutexWoken
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if atomic.CompareAndSwapInt32(&m.state, old, new) {
? ? ? ? ? ? ? ? ? ? // mutex 處于未加鎖,正常模式下,當(dāng)前 goroutine 獲得鎖
? ? ? ? ? ? ? ? ? ? ? ? if old&(mutexLocked|mutexStarving) == 0 {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break // locked the mutex with CAS
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? // 如果已經(jīng)在排隊了,就排到隊伍的最前面
? ? ? ? ? ? ? ? ? ? ? ? queueLifo := waitStartTime != 0
? ? ? ? ? ? ? ? ? ? ? ? if waitStartTime == 0 {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? waitStartTime = runtime_nanotime()
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? // queueLifo 為真的時候,當(dāng)前goroutine會被放到隊頭,
? ? ? ? ? ? ? ? ? ? ? ? // 也就是說被喚醒卻沒搶到鎖的goroutine放到最前面
? ? ? ? ? ? ? ? ? ? ? ? runtime_SemacquireMutex(&m.sema, queueLifo)
? ? ? ? ? ? ? ? ? ? ? ? // 當(dāng)前goroutine等待時間超過閾值,切換為饑餓模式,starving設(shè)置為true
? ? ? ? ? ? ? ? ? ? ? ? starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs
? ? ? ? ? ? ? ? ? ? ? ? old = m.state
? ? ? ? ? ? ? ? ? ? ? ? //如果當(dāng)前是饑餓模式
? ? ? ? ? ? ? ? ? ? ? ? if old&mutexStarving != 0 {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? throw("sync: inconsistent mutex state")
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 如果切換為饑餓模式,等待隊列計數(shù)減1
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? delta := int32(mutexLocked - 1<<mutexWaiterShift)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 如果等待時間小于1ms或者自己是最后一個被喚醒的,退出饑餓模式
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if !starving || old>>mutexWaiterShift == 1 {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? delta -= mutexStarving
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? atomic.AddInt32(&m.state, delta)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? awoke = true
? ? ? ? ? ? ? ? ? ? ? ? iter = 0
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? old = m.state
? ? ? ? ? ? ? ? }
? ? ? ? }

? ? ? ? if race.Enabled {
? ? ? ? ? ? ? ? race.Acquire(unsafe.Pointer(m))
? ? ? ? }
}

UnLock 解鎖分兩步

  • 解鎖,通過CAS操作把當(dāng)前狀態(tài)設(shè)置為解鎖狀態(tài)
  • 喚醒休眠協(xié)程,CAS操作把當(dāng)前狀態(tài)的waiter數(shù)減1,然后喚醒休眠goroutine,如果是饑餓模式的話,喚醒等待隊列的第一個
func (m *Mutex) Unlock() {
? ? ? ? if race.Enabled {
? ? ? ? ? ? ? ? _ = m.state
? ? ? ? ? ? ? ? race.Release(unsafe.Pointer(m))
? ? ? ? }

? ? ? ? new := atomic.AddInt32(&m.state, -mutexLocked)
? ? ? ? if (new+mutexLocked)&mutexLocked == 0 {
? ? ? ? ? ? ? ? throw("sync: unlock of unlocked mutex")
? ? ? ? }

? ? ? ? if new&mutexStarving == 0 {
? ? ? ? // 正常模式
? ? ? ? ? ? ? ? old := new
? ? ? ? ? ? ? ? for {
? ? ? ? ? ? ? ? ? ? /**
? ? ? ? ? ? ?* 不需要喚醒的情況
? ? ? ? ? ? ?* 1.等待隊列為0
? ? ? ? ? ? ?* 2.已經(jīng)有協(xié)程搶到鎖(上面的瞬間搶鎖)
? ? ? ? ? ? ?* 3.已經(jīng)有協(xié)程被喚醒
? ? ? ? ? ? ?* 4.處于饑餓模式 在饑餓模式獲取到鎖的協(xié)程仍然處于饑餓狀態(tài),新的goroutine無法獲取到鎖
? ? ? ? ? ? ?*/
? ? ? ? ? ? ? ? ? ? ? ? if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? // Grab the right to wake someone.
? ? ? ? ? ? ? ? ? ? ? ? new = (old - 1<<mutexWaiterShift) | mutexWoken
? ? ? ? ? ? ? ? ? ? ? ? if atomic.CompareAndSwapInt32(&m.state, old, new) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? runtime_Semrelease(&m.sema, false)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? old = m.state
? ? ? ? ? ? ? ? }
? ? ? ? } else {
? ? ? ? ? ? // 饑餓模式
? ? ? ? ? ? ? ? runtime_Semrelease(&m.sema, true)
? ? ? ? }
}

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

相關(guān)文章

  • golang使用正則表達(dá)式解析網(wǎng)頁

    golang使用正則表達(dá)式解析網(wǎng)頁

    這篇文章主要介紹了golang使用正則表達(dá)式解析網(wǎng)頁,需要的朋友可以參考下
    2015-03-03
  • Go Module依賴管理的實現(xiàn)

    Go Module依賴管理的實現(xiàn)

    Go Module是Go語言的官方依賴管理解決方案,其提供了一種簡單、可靠的方式來管理項目的依賴關(guān)系,本文主要介紹了Go Module依賴管理的實現(xiàn),感興趣的可以了解一下
    2024-06-06
  • 詳解golang中的結(jié)構(gòu)體編解碼神器Mapstructure庫

    詳解golang中的結(jié)構(gòu)體編解碼神器Mapstructure庫

    mapstructure是GO字典(map[string]interface{})和Go結(jié)構(gòu)體之間轉(zhuǎn)換的編解碼工具,這篇文章主要為大家介紹一下Mapstructure庫的相關(guān)使用,希望對大家有所幫助
    2023-09-09
  • 一文帶你了解Go語言標(biāo)準(zhǔn)庫strings的常用函數(shù)和方法

    一文帶你了解Go語言標(biāo)準(zhǔn)庫strings的常用函數(shù)和方法

    strings?庫包含了許多高效的字符串常用操作的函數(shù)和方法,巧用這些函數(shù)與方法,能極大的提高我們程序的性能。本文就來和大家分享一下Go標(biāo)準(zhǔn)庫strings的常用函數(shù)和方法,希望對大家有所幫助
    2022-11-11
  • 再次探討go實現(xiàn)無限 buffer 的 channel方法

    再次探討go實現(xiàn)無限 buffer 的 channel方法

    我們知道go語言內(nèi)置的channel緩沖大小是有上限的,那么我們自己如何實現(xiàn)一個無限 buffer 的 channel呢?今天通過本文給大家分享go實現(xiàn)無限 buffer 的 channel方法,感興趣的朋友一起看看吧
    2021-06-06
  • 基于go中fyne gui的通達(dá)信數(shù)據(jù)導(dǎo)出工具詳解

    基于go中fyne gui的通達(dá)信數(shù)據(jù)導(dǎo)出工具詳解

    這篇文章主要介紹了基于go中fyne gui的通達(dá)信數(shù)據(jù)導(dǎo)出工具,這是一個用 Go 語言開發(fā)的通達(dá)信數(shù)據(jù)導(dǎo)出工具,可以將通達(dá)信的本地數(shù)據(jù)導(dǎo)出為多種格式,方便用戶進(jìn)行數(shù)據(jù)分析和處理,需要的朋友可以參考下
    2024-12-12
  • golang gorm多條件篩選查詢操作

    golang gorm多條件篩選查詢操作

    這篇文章主要介紹了golang gorm多條件篩選查詢操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go源碼字符串規(guī)范檢查lint工具strchecker使用詳解

    Go源碼字符串規(guī)范檢查lint工具strchecker使用詳解

    這篇文章主要為大家介紹了Go源碼字符串規(guī)范檢查lint工具strchecker使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go語言LeetCode500鍵盤行題解示例詳解

    Go語言LeetCode500鍵盤行題解示例詳解

    這篇文章主要為大家介紹了Go語言LeetCode500鍵盤行題解示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • go并發(fā)編程sync.Cond使用場景及實現(xiàn)原理

    go并發(fā)編程sync.Cond使用場景及實現(xiàn)原理

    這篇文章主要為大家介紹了go并發(fā)編程sync.Cond使用場景及實現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08

最新評論