golang mutex互斥锁分析
互斥锁:没有读锁写锁之分,同一时刻,只能有一个gorutine获取一把锁
数据结构设计:
type Mutex struct {
state int32 // 将一个32位整数拆分为 当前阻塞的goroutine数(30位)|唤醒状态(1位)|锁状态(1位) 的形式,来简化字段设计
sema uint32 // 信号量
} const (
mutexLocked = << 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返回false
if atomic.CompareAndSwapInt32(&m.state, , mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
} awoke := false
iter :=
for {
old := m.state // 保存对象当前锁状态
new := old | mutexLocked // 保存对象即将被设置成的状态
if old&mutexLocked != { // 判断对象是否处于锁定状态
if runtime_canSpin(iter) { // 判断当前goroutine是否可以进入自旋锁
if !awoke && old&mutexWoken == && old>>mutexWaiterShift != &&
atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
awoke = true
}
runtime_doSpin() // 进入自旋锁后当前goroutine并不挂起,仍然在占用cpu资源,所以重试一定次数后,不会再进入自旋锁逻辑
iter++
continue
}
// 更新阻塞goroutine的数量
new = old + <<mutexWaiterShift //new = 2
}
if awoke {
if new&mutexWoken == {
panic("sync: inconsistent mutex state")
}
// 设置唤醒状态位0
new &^= mutexWoken
}
if atomic.CompareAndSwapInt32(&m.state, old, new) {
if old&mutexLocked == { // 如果对象原本不是锁定状态,对象已经获取到了锁
break
}
// 如果对象原本就是锁定状态,挂起当前goroutine,进入休眠等待状态
runtime_Semacquire(&m.sema)
awoke = true
iter =
}
} if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
}
再来看看unlock函数,终于可以来点轻松的了
func (m *Mutex) Unlock() {
if race.Enabled {
_ = m.state
race.Release(unsafe.Pointer(m))
} // 改变锁的状态值
new := atomic.AddInt32(&m.state, -mutexLocked)
if (new+mutexLocked)&mutexLocked == { // 如果原来锁不是锁定状态,报错
panic("sync: unlock of unlocked mutex")
} old := new
for {
// 1. 如果没有阻塞的goroutine直接返回
// 2. 如果已经被其他goroutine获取到锁了直接返回
// 需要说明的是为什么是old&(mutexLocked|mutexWoken)而不是old&mutexLocked
// 首先如果是old&mutexLocked的话,那锁就没法释放了
// 最主要的一点是lock时进入自旋锁逻辑后,goroutine持有的对象状态会设置为mutexLocked|mutexWoken
// 这种情况让再去解锁后mutexWaiterShift数就会出现不一致情况
if old>>mutexWaiterShift == || old&(mutexLocked|mutexWoken) != {
return
}
// 更新阻塞的goroutine个数
new = (old - <<mutexWaiterShift) | mutexWoken
if atomic.CompareAndSwapInt32(&m.state, old, new) {
// 通知阻塞的goroutine
runtime_Semrelease(&m.sema)
return
}
old = m.state
}
}
总结:
一、互斥效果实现方式
1. 当前goroutine进入自旋锁逻辑等待中
2. 挂起当前goroutine等待其他goroutine解锁通知,通过信号量实现
二、锁数据结构设计十分精简
goroutine数(30位)|唤醒状态(1位)|锁状态(1位)
golang mutex互斥锁分析的更多相关文章
- RWLock——一种细粒度的Mutex互斥锁
RWMutex -- 细粒度的读写锁 我们之前有讲过 Mutex 互斥锁.这是在任何时刻下只允许一个 goroutine 执行的串行化的锁.而现在这个 RWMutex 就是在 Mutex 的基础上进行 ...
- Go 标准库 —— sync.Mutex 互斥锁
Mutex 是一个互斥锁,可以创建为其他结构体的字段:零值为解锁状态.Mutex 类型的锁和线程无关,可以由不同的线程加锁和解锁. 方法 func (*Mutex) Lock func (m *Mut ...
- C# Mutex互斥锁
Mutex 构造函数 (Boolean, String, Boolean) public Mutex ( bool initiallyOwned, string name, out bool crea ...
- C# mutex互斥锁构造
概念 Mutext 出现的比monitor更早,而且传承自COM,当然,waitHandle也是它的父类,它继承了其父类的功能,有趣的是Mutex的脾气非常的古怪,它 允许同一个线程多次重复访问共享区 ...
- 【转】【C#】【Thread】Mutex 互斥锁
Mutex:互斥(体) 又称同步基元. 当创建一个应用程序类时,将同时创建一个系统范围内的命名的Mutex对象.这个互斥元在整个操作系统中都是可见的.当已经存在一个同名的互斥元时,构造函数将会输出一个 ...
- Golang 读写锁RWMutex 互斥锁Mutex 源码详解
前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...
- 互斥锁Mutex与信号量Semaphore的区别
转自互斥锁Mutex与信号量Semaphore的区别 多线程编程中,常常会遇到这两个概念:Mutex和Semaphore,两者之间区别如下: 有人做过如下类比: Mutex是一把钥匙,一个人拿了就可进 ...
- 探索互斥锁 Mutex 实现原理
Mutex 互斥锁 概要描述 mutex 是 go 提供的同步原语.用于多个协程之间的同步协作.在大多数底层框架代码中都会用到这个锁. mutex 总过有三个状态 mutexLocked: 表示占有锁 ...
- golang RWMutex读写锁分析
RWMutex:是基于Mutex实现的读写互斥锁,一个goroutine可以持有多个读锁或者一个写锁,同一时刻只能持有读锁或者写锁 数据结构设计: type RWMutex struct { w Mu ...
随机推荐
- Condition源码分析
转:http://www.nbtarena.com/Html/soft/201308/2429.html Condition的概念 大体实现流程 I.初始化状态 II.await()*作 III.si ...
- C++编程练习(9)----“图的存储结构以及图的遍历“(邻接矩阵、深度优先遍历、广度优先遍历)
图的存储结构 1)邻接矩阵 用两个数组来表示图,一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中边或弧的信息. 2)邻接表 3)十字链表 4)邻接多重表 5)边集数组 本文只用代码实现用 ...
- windows下安装redis以及redis扩展,设置redis为windows自启服务
windows下安装reids windows下redis下载地址:https://github.com/MSOpenTech/redis/releases. 启动redis服务:在redis目录下启 ...
- 【吐血整理】svn命令行,Subversion的正确使用姿势~
一.写在前面 前面一直博主一直用svn的桌面版本,但看项目经理一直都用的命令行方式,不为性能,还能直接装逼呀!在这里先感谢赵哥,也把它分享给感兴趣的你们~ 二.直接上干货 1. svn checkou ...
- (五)Lua脚本语言入门
---恢复内容开始--- 写完这篇Lua脚本语言入门,自己就要尝试去用Lua脚本语言写esp8266了,,自己现在挺心急的,因为朋友使用esp8266本来说自己帮忙写好程序的,但是用的单片机不一样自己 ...
- selenium框架与chrome浏览器的不兼容问题
在一次偶然的情况下,在chrome上用selenium框架去抓取某个id为XX的页面元素,使用WebDriver的findElement().click()方法进行点击,原来在firefox浏览器运行 ...
- Python学习一:Python简介
Python简介: Python是目前广泛使用的一门动态语言,类似Java,源代码必须首先由编译器转换成字节码(byte code),然后再由解释器来执行字节码.与Java不同的是,Python的编译 ...
- android学习13——android egl hello world
通常情况下我们使用GLSurfaceview来实现opengl渲染.GLSurfaceview实现上是对SurfaceView和EGL的封装.为了从本质上理解渲染流程,使用EGL和SurfaceVie ...
- Android Http请求头与响应头的学习
本节引言: 上节中我们对Android涉及的网络编程进行了了解,也学习了下Http的基本概念,而本节我们 要学习的是Http的请求头与响应头,当然,可以把也可以把这节看作文档,用到的时候来查查 即可! ...
- 项目架构开发:数据访问层之Repository
接上文 项目架构开发:数据访问层之Logger 本章我们继续IRepository开发,这个仓储与领域模式里边的仓储有区别,更像一个工具类,也就是有些园友说的“伪仓储”, 这个仓储只实现单表的CURD ...