浅析Golang怎么实现锁
来源:亿速云
2023-03-15 17:12:13
0浏览
收藏
对于一个Golang开发者来说,牢固扎实的基础是十分重要的,golang学习网就来带大家一点点的掌握基础知识点。今天本篇文章带大家了解《浅析Golang怎么实现锁》,主要介绍了go语言,希望对大家的知识积累有所帮助,快点收藏起来吧,否则需要时就找不到了!
今天小编给大家分享一下浅析Golang怎么实现锁的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。
Lock
// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
// 上锁,成功返回
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
// Slow path (outlined so that the fast path can be inlined)
//已经锁上的写成进入慢锁流程
m.lockSlow()
}lockSlow
func (m *Mutex) lockSlow() {
var waitStartTime int64 //执行时间
starving := false //当前请求是否是饥饿模式
awoke := false //当前请求是否是唤醒状态
iter := 0 //自旋次数
old := m.state //旧state值
for {
// Don't spin in starvation mode, ownership is handed off to waiters
// so we won't be able to acquire the mutex anyway.
//旧state值已上锁,并且未进入饥饿模式,且可以自旋,进入自旋逻辑
if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {
// Active spinning makes sense.
// Try to set mutexWoken flag to inform Unlock
// to not wake other blocked goroutines.
// 当前协程未唤醒
//&& old.state 为未唤起状态,就是说没有其他被唤起的waiter
//&& waiter数>0
//&& m.state标记为唤起状态成功
if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
//标记当前协程为唤起状态
//r: 这是为了通知在解锁Unlock()中不要再唤醒其他的waiter了
awoke = true
}
//自旋
runtime_doSpin()
//自旋计数器
iter++
old = m.state
continue
}
//r: old是锁当前的状态,new是期望的状态,以期于在后面的CAS操作中更改锁的状态
//new代表期望的state值
new := old
// Don't try to acquire starving mutex, new arriving goroutines must queue.
//old不是饥饿状态,new带上上锁标志位,也就是饥饿状态不上锁
if old&mutexStarving == 0 {
new |= mutexLocked
}
//旧state值是上锁状态或饥饿状态,新state waiter数+1
//r: 表示当前goroutine将被作为waiter置于等待队列队尾
if old&(mutexLocked|mutexStarving) != 0 {
new += 1 starvationThresholdNs
// 再次获取锁状态
old = m.state
if old&mutexStarving != 0 {
// If this goroutine was woken and mutex is in starvation mode,
// ownership was handed off to us but mutex is in somewhat
// inconsistent state: mutexLocked is not set and we are still
// accounted as waiter. Fix that.
//饥饿模式协程是在Unlock()时handoff到当前协程的
//r:? 如果当前锁既不是被获取也不是被唤醒状态,或者等待队列为空
// 这代表锁状态产生了不一致的问题
if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {
throw("sync: inconsistent mutex state")
}
//m.state 上锁,waiter数-1
delta := int32(mutexLocked - 1>mutexWaiterShift == 1 {
// Exit starvation mode.
// Critical to do it here and consider wait time.
// Starvation mode is so inefficient, that two goroutines
// can go lock-step infinitely once they switch mutex
// to starvation mode.
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
// Unlock unlocks m.
// It is a run-time error if m is not locked on entry to Unlock.
//
// A locked Mutex is not associated with a particular goroutine.
// It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it.
func (m *Mutex) Unlock() {
if race.Enabled {
_ = m.state
race.Release(unsafe.Pointer(m))
}
// Fast path: drop lock bit.
//m.state取消锁状态,返回值new代表修改后的新值
//如果为0代表没有其他锁了,退出;否则进入unlockSlow()
//锁空闲有两种情况:
//1. 所有位为0,代表没有锁了
//2. 标志位为0, waiter数量>0,还有协程在等待解锁
new := atomic.AddInt32(&m.state, -mutexLocked)
if new != 0 {
// Outlined slow path to allow inlining the fast path.
// To hide unlockSlow during tracing we skip one extra frame when tracing GoUnblock.
m.unlockSlow(new)
}
}UnlockSlow
func (m *Mutex) unlockSlow(new int32) {
if (new+mutexLocked)&mutexLocked == 0 {
throw("sync: unlock of unlocked mutex")
}
if new&mutexStarving == 0 {
old := new
for {
// If there are no waiters or a goroutine has already
// been woken or grabbed the lock, no need to wake anyone.
// In starvation mode ownership is directly handed off from unlocking
// goroutine to the next waiter. We are not part of this chain,
// since we did not observe mutexStarving when we unlocked the mutex above.
// So get off the way.
//解锁,结束,退出
//1. 没有waiter了
//2. 已上锁
//3. 锁处于唤醒状态,表示有协程被唤醒
//4. 饥饿模式, 所有权交给了被解锁饥饿模式的waiter
if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {
return
}
// Grab the right to wake someone.
// 如果能走到这,那就是上面的if判断没通过
// 说明当前锁是空闲状态,但是等待队列中有waiter,且没有goroutine被唤醒
// 所以,这里我们想要把锁的状态设置为被唤醒,等待队列waiter数-1
new = (old - 1其他关键函数runtime_canSpin是否可自旋,不展开runtime_doSpin核心是汇编实现,循环执行三十次PAUSE指令runtime_SemacquireMutex信号量上锁sem来自单词semaphore 信号量runtime_Semrelease信号量释放func runtime_Semrelease(s *uint32, handoff bool, skipframes int)If handoff is true, pass count directly to the first waiter.handoff 就是传球的意思,handoff 为 false 时,仅仅唤醒等待队列中第一个协程,但是不会立马调度该协程;当 handoff 为 true 时,会立马调度被唤醒的协程,此外,当 handoff = true 时,被唤醒的协程会继承当前协程的时间片。具体例子,假设每个 goroutine 的时间片为 2ms,gorounte A 已经执行了 1ms,假设它通过 runtime_Semrelease(handoff = true) 唤醒了 goroutine B,则 goroutine B 剩余的时间片为 2 - 1 = 1ms。golang 中 sync.Mutex 的实现semrelease1(addr, handoff, skipframes) 参数handoff若为true,则让被唤醒的g立刻继承当前g的时间片继续执行。若handoff为false,则把刚被唤醒的g放到当前p的runq中。Golang sync.Mutex 源码分析RWMutex很简单,看源码就行[Go并发] - RWMutex源码解析type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers 当前读锁数量
readerWait int32 // number of departing readers 要离开的读锁数量,暨等待写锁解锁,解锁后可以释放的读锁数量
}Lock()// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (rw *RWMutex) Lock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// First, resolve competition with other writers.
rw.w.Lock() //通过sync.Lock()限制多写锁进入下边的逻辑
// Announce to readers there is a pending writer.
//r值不变, rwmutexMaxReaders值为1UnLock()// Unlock unlocks rw for writing. It is a run-time error if rw is
// not locked for writing on entry to Unlock.
//
// As with Mutexes, a locked RWMutex is not associated with a particular
// goroutine. One goroutine may RLock (Lock) a RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {
if race.Enabled {
_ = rw.w.state
race.Release(unsafe.Pointer(&rw.readerSem))
race.Disable()
}
// Announce to readers there is no active writer.
//将Lock()方法减去的值加回来,变成正数
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
race.Enable()
throw("sync: Unlock of unlocked RWMutex")
}
// Unblock blocked readers, if any.
//唤醒在RLock()方法阻塞的读操作,数量为r
for i := 0; i RLock()// RLock locks rw for reading.
//
// It should not be used for recursive read locking; a blocked Lock
// call excludes new readers from acquiring the lock. See the
// documentation on the RWMutex type.
func (rw *RWMutex) RLock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
//UnRLock()// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {
if race.Enabled {
_ = rw.w.state
race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
race.Disable()
}
//以上就是“浅析Golang怎么实现锁”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注golang学习网行业资讯频道。终于介绍完啦!小伙伴们,这篇关于《浅析Golang怎么实现锁》的介绍应该让你收获多多了吧!欢迎大家收藏或分享给更多需要学习的朋友吧~golang学习网公众号也会发布Golang相关知识,快来关注吧! 版本声明
本文转载于:亿速云 如有侵犯,请联系study_golang@163.com删除
一文聊聊Golang中内存管理逃逸的方法
- 上一篇
- 一文聊聊Golang中内存管理逃逸的方法
- 下一篇
- 浅析如何统计一个表的数据量
查看更多
最新文章
-
- Golang · Go教程 | 6小时前 |
- Golang微服务超时处理技巧分享
- 168浏览 收藏
-
- Golang · Go教程 | 6小时前 |
- Golang高精度定时器选择与优化技巧
- 364浏览 收藏
-
- Golang · Go教程 | 6小时前 |
- Golang并发日志写入优化方案
- 119浏览 收藏
-
- Golang · Go教程 | 6小时前 | golang 生产者消费者
- Golangchannel生产者消费者示例详解
- 188浏览 收藏
-
- Golang · Go教程 | 6小时前 |
- Golang异步任务测试技巧分享
- 353浏览 收藏
-
- Golang · Go教程 | 6小时前 |
- Golang构建Docker镜像步骤详解
- 416浏览 收藏
-
- Golang · Go教程 | 6小时前 |
- GolangBenchmark优化技巧全解析
- 437浏览 收藏
-
- Golang · Go教程 | 6小时前 |
- GoHTTP处理器结构化开发指南
- 381浏览 收藏
-
- Golang · Go教程 | 7小时前 |
- GolangRESTAPI错误响应规范详解
- 136浏览 收藏
-
- Golang · Go教程 | 7小时前 |
- Golang接口是否包含方法判断方法
- 299浏览 收藏
-
- Golang · Go教程 | 7小时前 |
- Golang桥接模式详解与实现方法
- 317浏览 收藏
-
- Golang · Go教程 | 7小时前 |
- Go语言实现多字节Unicode的N-gram方法
- 360浏览 收藏
查看更多
课程推荐
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
查看更多
AI推荐
-
- ChatExcel酷表
- ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
- 3296次使用
-
- Any绘本
- 探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
- 3505次使用
-
- 可赞AI
- 可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
- 3537次使用
-
- 星月写作
- 星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
- 4650次使用
-
- MagicLight
- MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
- 3914次使用
查看更多
相关文章
-
- 有关Go语言拼接URL路径的方法
- 2023-03-09 185浏览
-
- go语言能不能做后端
- 2023-03-03 460浏览
-
- go语言和java的区别是什么
- 2023-03-03 430浏览
-
- go语言如何进行强制类型转换
- 2023-03-04 450浏览
-
- go语言的beego怎么使用
- 2023-03-03 320浏览

