當(dāng)前位置:
首頁 >
golang mutex互斥锁分析
發(fā)布時(shí)間:2025/3/15
25
豆豆
生活随笔
收集整理的這篇文章主要介紹了
golang mutex互斥锁分析
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
互斥鎖:沒有讀鎖寫鎖之分,同一時(shí)刻,只能有一個(gè)gorutine獲取一把鎖
數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì):
type Mutex struct {state int32 // 將一個(gè)32位整數(shù)拆分為 當(dāng)前阻塞的goroutine數(shù)(30位)|喚醒狀態(tài)(1位)|鎖狀態(tài)(1位) 的形式,來簡化字段設(shè)計(jì)sema uint32 // 信號(hào)量 }const (mutexLocked = 1 << iota // 0001 含義:用最后一位表示當(dāng)前對象鎖的狀態(tài),0-未鎖住 1-已鎖住mutexWoken // 0010 含義:用倒數(shù)第二位表示當(dāng)前對象是否被喚醒 0-喚醒 1-未喚醒mutexWaiterShift = iota // 2 含義:從倒數(shù)第二位往前的bit位表示在排隊(duì)等待的goroutine數(shù) )關(guān)鍵函數(shù)設(shè)計(jì):
lock函數(shù):
//獲取鎖,如果鎖已經(jīng)在使用,則會(huì)阻塞一直到鎖可用 func (m *Mutex) Lock() {// m.sate == 0時(shí)說明當(dāng)前對象還沒有被鎖住過,進(jìn)行原子操賦值作操作設(shè)置mutexLocked狀態(tài),CompareAnSwapInt32返回true// m.sate != 1時(shí)剛好相反說明對象已被其他goroutine鎖住,不會(huì)進(jìn)行原子賦值操作設(shè)置,CopareAndSwapInt32返回falseif atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { if race.Enabled {race.Acquire(unsafe.Pointer(m))}return}awoke := falseiter := 0for {old := m.state // 保存對象當(dāng)前鎖狀態(tài)new := old | mutexLocked // 保存對象即將被設(shè)置成的狀態(tài)if old&mutexLocked != 0 { // 判斷對象是否處于鎖定狀態(tài)if runtime_canSpin(iter) { // 判斷當(dāng)前goroutine是否可以進(jìn)入自旋鎖if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {awoke = true}runtime_doSpin() // 進(jìn)入自旋鎖后當(dāng)前goroutine并不掛起,仍然在占用cpu資源,所以重試一定次數(shù)后,不會(huì)再進(jìn)入自旋鎖邏輯iter++continue}// 更新阻塞goroutine的數(shù)量new = old + 1<<mutexWaiterShift //new = 2 }if awoke {if new&mutexWoken == 0 {panic("sync: inconsistent mutex state")}// 設(shè)置喚醒狀態(tài)位0new &^= mutexWoken}if atomic.CompareAndSwapInt32(&m.state, old, new) {if old&mutexLocked == 0 { // 如果對象原本不是鎖定狀態(tài),對象已經(jīng)獲取到了鎖break}// 如果對象原本就是鎖定狀態(tài),掛起當(dāng)前goroutine,進(jìn)入休眠等待狀態(tài)runtime_Semacquire(&m.sema)awoke = trueiter = 0}}if race.Enabled {race.Acquire(unsafe.Pointer(m))} }再來看看unlock函數(shù),終于可以來點(diǎn)輕松的了
func (m *Mutex) Unlock() {if race.Enabled {_ = m.staterace.Release(unsafe.Pointer(m))}// 改變鎖的狀態(tài)值new := atomic.AddInt32(&m.state, -mutexLocked)if (new+mutexLocked)&mutexLocked == 0 { // 如果原來鎖不是鎖定狀態(tài),報(bào)錯(cuò)panic("sync: unlock of unlocked mutex")}old := newfor {// 1. 如果沒有阻塞的goroutine直接返回// 2. 如果已經(jīng)被其他goroutine獲取到鎖了直接返回// 需要說明的是為什么是old&(mutexLocked|mutexWoken)而不是old&mutexLocked
// 首先如果是old&mutexLocked的話,那鎖就沒法釋放了
// 最主要的一點(diǎn)是lock時(shí)進(jìn)入自旋鎖邏輯后,goroutine持有的對象狀態(tài)會(huì)設(shè)置為mutexLocked|mutexWoken
// 這種情況讓再去解鎖后mutexWaiterShift數(shù)就會(huì)出現(xiàn)不一致情況if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {return}// 更新阻塞的goroutine個(gè)數(shù)new = (old - 1<<mutexWaiterShift) | mutexWokenif atomic.CompareAndSwapInt32(&m.state, old, new) {// 通知阻塞的goroutineruntime_Semrelease(&m.sema)return}old = m.state} }
?
總結(jié):
一、互斥效果實(shí)現(xiàn)方式
1. 當(dāng)前goroutine進(jìn)入自旋鎖邏輯等待中
2. 掛起當(dāng)前goroutine等待其他goroutine解鎖通知,通過信號(hào)量實(shí)現(xiàn)
二、鎖數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)十分精簡
? ? ?goroutine數(shù)(30位)|喚醒狀態(tài)(1位)|鎖狀態(tài)(1位)
轉(zhuǎn)載于:https://www.cnblogs.com/zongjiang/p/6577297.html
總結(jié)
以上是生活随笔為你收集整理的golang mutex互斥锁分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jQuery解决高度统一问题
- 下一篇: UAC 实现原理及绕过方法