欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

golang mutex互斥锁分析

发布时间:2025/3/15 编程问答 29 豆豆
生活随笔 收集整理的这篇文章主要介绍了 golang mutex互斥锁分析 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

互斥锁:没有读锁写锁之分,同一时刻,只能有一个gorutine获取一把锁

数据结构设计:

type Mutex struct {state int32 // 将一个32位整数拆分为 当前阻塞的goroutine数(30位)|唤醒状态(1位)|锁状态(1位) 的形式,来简化字段设计sema uint32 // 信号量 }const (mutexLocked = 1 << iota // 0001 含义:用最后一位表示当前对象锁的状态,0-未锁住 1-已锁住mutexWoken // 0010 含义:用倒数第二位表示当前对象是否被唤醒 0-唤醒 1-未唤醒mutexWaiterShift = iota // 2 含义:从倒数第二位往前的bit位表示在排队等待的goroutine数 )

关键函数设计:

lock函数:

//获取锁,如果锁已经在使用,则会阻塞一直到锁可用 func (m *Mutex) Lock() {// m.sate == 0时说明当前对象还没有被锁住过,进行原子操赋值作操作设置mutexLocked状态,CompareAnSwapInt32返回true// m.sate != 1时刚好相反说明对象已被其他goroutine锁住,不会进行原子赋值操作设置,CopareAndSwapInt32返回falseif atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { if race.Enabled {race.Acquire(unsafe.Pointer(m))}return}awoke := falseiter := 0for {old := m.state // 保存对象当前锁状态new := old | mutexLocked // 保存对象即将被设置成的状态if old&mutexLocked != 0 { // 判断对象是否处于锁定状态if runtime_canSpin(iter) { // 判断当前goroutine是否可以进入自旋锁if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {awoke = true}runtime_doSpin() // 进入自旋锁后当前goroutine并不挂起,仍然在占用cpu资源,所以重试一定次数后,不会再进入自旋锁逻辑iter++continue}// 更新阻塞goroutine的数量new = old + 1<<mutexWaiterShift //new = 2 }if awoke {if new&mutexWoken == 0 {panic("sync: inconsistent mutex state")}// 设置唤醒状态位0new &^= mutexWoken}if atomic.CompareAndSwapInt32(&m.state, old, new) {if old&mutexLocked == 0 { // 如果对象原本不是锁定状态,对象已经获取到了锁break}// 如果对象原本就是锁定状态,挂起当前goroutine,进入休眠等待状态runtime_Semacquire(&m.sema)awoke = trueiter = 0}}if race.Enabled {race.Acquire(unsafe.Pointer(m))} }

再来看看unlock函数,终于可以来点轻松的了

func (m *Mutex) Unlock() {if race.Enabled {_ = m.staterace.Release(unsafe.Pointer(m))}// 改变锁的状态值new := atomic.AddInt32(&m.state, -mutexLocked)if (new+mutexLocked)&mutexLocked == 0 { // 如果原来锁不是锁定状态,报错panic("sync: unlock of unlocked mutex")}old := newfor {// 1. 如果没有阻塞的goroutine直接返回// 2. 如果已经被其他goroutine获取到锁了直接返回
// 需要说明的是为什么是old&(mutexLocked|mutexWoken)而不是old&mutexLocked
// 首先如果是old&mutexLocked的话,那锁就没法释放了
// 最主要的一点是lock时进入自旋锁逻辑后,goroutine持有的对象状态会设置为mutexLocked|mutexWoken
// 这种情况让再去解锁后mutexWaiterShift数就会出现不一致情况
if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {return}// 更新阻塞的goroutine个数new = (old - 1<<mutexWaiterShift) | mutexWokenif atomic.CompareAndSwapInt32(&m.state, old, new) {// 通知阻塞的goroutineruntime_Semrelease(&m.sema)return}old = m.state} }

 

总结:

一、互斥效果实现方式

  1. 当前goroutine进入自旋锁逻辑等待中

  2. 挂起当前goroutine等待其他goroutine解锁通知,通过信号量实现

二、锁数据结构设计十分精简

     goroutine数(30位)|唤醒状态(1位)|锁状态(1位)

转载于:https://www.cnblogs.com/zongjiang/p/6577297.html

总结

以上是生活随笔为你收集整理的golang mutex互斥锁分析的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。