详解Golang互斥锁内部实现
来源:脚本之家
时间:2022-12-24 11:07:14 433浏览 收藏
本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《详解Golang互斥锁内部实现》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~
go语言提供了一种开箱即用的共享资源的方式,互斥锁(sync.Mutex), sync.Mutex的零值表示一个没有被锁的,可以直接使用的,一个goroutine获得互斥锁后其他的goroutine只能等到这个gorutine释放该互斥锁,在Mutex结构中只公开了两个函数,分别是Lock和Unlock,在使用互斥锁的时候非常简单,本文并不阐述使用。
在使用sync.Mutex的时候千万不要做值拷贝,因为这样可能会导致锁失效。当我们打开我们的IDE时候跳到我们的sync.Mutex 代码中会发现它有如下的结构:
type Mutex struct { state int32 //互斥锁上锁状态枚举值如下所示 sema uint32 //信号量,向处于Gwaitting的G发送信号 } const ( mutexLocked = 1上面的state值分别为 0(可用) 1(被锁) 2~31等待队列计数
下面是互斥锁的源码,这里会有四个比较重要的方法需要提前解释,分别是runtime_canSpin,runtime_doSpin,runtime_SemacquireMutex,runtime_Semrelease,
1、runtime_canSpin:比较保守的自旋,golang中自旋锁并不会一直自旋下去,在runtime包中runtime_canSpin方法做了一些限制, 传递过来的iter大等于4或者cpu核数小等于1,最大逻辑处理器大于1,至少有个本地的P队列,并且本地的P队列可运行G队列为空。
//go:linkname sync_runtime_canSpin sync.runtime_canSpin func sync_runtime_canSpin(i int) bool { if i >= active_spin || ncpu2、 runtime_doSpin:会调用procyield函数,该函数也是汇编语言实现。函数内部循环调用PAUSE指令。PAUSE指令什么都不做,但是会消耗CPU时间,在执行PAUSE指令时,CPU不会对它做不必要的优化。
//go:linkname sync_runtime_doSpin sync.runtime_doSpin func sync_runtime_doSpin() { procyield(active_spin_cnt) }3、runtime_SemacquireMutex:
//go:linkname sync_runtime_SemacquireMutex sync.runtime_SemacquireMutex func sync_runtime_SemacquireMutex(addr *uint32) { semacquire(addr, semaBlockProfile|semaMutexProfile) }4、runtime_Semrelease:
//go:linkname sync_runtime_Semrelease sync.runtime_Semrelease func sync_runtime_Semrelease(addr *uint32) { semrelease(addr) } Mutex的Lock函数定义如下 func (m *Mutex) Lock() { //先使用CAS尝试获取锁 if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) { //这里是-race不需要管它 if race.Enabled { race.Acquire(unsafe.Pointer(m)) } //成功获取返回 return } awoke := false //循环标记 iter := 0 //循环计数器 for { old := m.state //获取当前锁状态 new := old | mutexLocked //将当前状态最后一位指定1 if old&mutexLocked != 0 { //如果所以被占用 if runtime_canSpin(iter) { //检查是否可以进入自旋锁 if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 && atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) { //awoke标记为true awoke = true } //进入自旋状态 runtime_doSpin() iter++ continue } //没有获取到锁,当前G进入Gwaitting状态 new = old + 1>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 { return } // 减少等待次数,添加清除标记 new = (old - 1互斥锁无冲突是最简单的情况了,有冲突时,首先进行自旋,,因为大多数的Mutex保护的代码段都很短,经过短暂的自旋就可以获得;如果自旋等待无果,就只好通过信号量来让当前Goroutine进入Gwaitting状态。
好了,本文到此结束,带大家了解了《详解Golang互斥锁内部实现》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!
-
200 收藏
-
312 收藏
-
117 收藏
-
446 收藏
-
426 收藏
-
233 收藏
-
322 收藏
-
181 收藏
-
316 收藏
-
244 收藏
-
300 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 难过的牛排
- 这篇文章内容出现的刚刚好,太详细了,写的不错,mark,关注师傅了!希望师傅能多写Golang相关的文章。
- 2023-01-31 09:20:32
-
- 朴素的蜡烛
- 很好,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢师傅分享技术文章!
- 2023-01-31 04:05:34
-
- 无奈的山水
- 太全面了,mark,感谢楼主的这篇博文,我会继续支持!
- 2023-01-27 17:03:08
-
- 柔弱的鸡
- 这篇博文真及时,很详细,写的不错,已加入收藏夹了,关注作者了!希望作者能多写Golang相关的文章。
- 2023-01-07 09:15:42
-
- 俊逸的枕头
- 赞 👍👍,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢博主分享博文!
- 2022-12-29 17:09:37
-
- 醉熏的大炮
- 这篇博文出现的刚刚好,很详细,真优秀,码起来,关注作者了!希望作者能多写Golang相关的文章。
- 2022-12-26 04:51:22