golang sync.RWMutex总结笔记
背景
最近项目中遇到两次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
}
RWMutex源码分析方面参考资料《go中的sync.RWMutex源码解读》写的比较详细和清晰易懂。
总结
go中锁都是不可重入的,所以同一个协程中获取两次RWmutex锁都可能出现死锁
1. 同一协程获取两次RLock
此场景参考前一篇文章《golang RWMutex RLock重入导致死锁》,同一协程两次获取RLock,如果在第二次获取RLock之前,有其他协程获取写锁Lock则会导致死锁。
这种场景和同一协程先获取Lock再获取RLock是一样的原理,由于协程1获取了RLock,导致协程2获取Lock时会被阻塞,然而协程2获取Lock时会将rw.readerCount置为<0的负值,然后协程1再获取RLock也会被阻塞,所以导致两个协程相互阻塞了。
2. 同一协程先获取RLock再获取Lock
func TestLockUp(t *testing.T) {
var l sync.RWMutex
l.RLock()
t.Log("acquire read lock")
l.Lock()
t.Log("acquire write lock")
l.Unlock()
l.RUnlock()
}
读锁是会阻塞写锁的;从源代码中可以看到,在获取Lock时,如果已经有协程获取了RLock,则获取Lock的协程会阻塞在获取rw.writerSem信号量上,在读锁RLock解锁时会唤醒该信号量,然后RLock在等待Lock解锁才能执行RUnLock,因此造成死锁。
3. 同一协程先获取Lock再获取RLock
func TestLockDown(t *testing.T) {
var l sync.RWMutex
l.Lock()
t.Log("acquire write lock")
l.RLock()
t.Log("acquire read lock")
l.RUnlock()
l.Unlock()
}
写锁也会阻止读锁;从源代码中可以看到,当一个协程获取写锁Lock时,会将rw.readerCount置为<0的负值,而当获取读锁RLock时,先对rw.readerCount加1,如果加1后的结果为负值,则表示有协程已经获取到写锁或者正在等待获取写锁,因而该获取读锁的协程会阻塞在获取rw.readerSem信号量上;因此同一个协程先后获取Lock和RLock会相互阻塞等待从而造成死锁。
从这里也能看出来,在RWMutex中,写锁的优先级高于读锁,只要有协程在等待获取写锁,后续的读锁都需要等待。
4. 同一协程获取两次Lock
func TestReLock(t *testing.T) {
var l sync.RWMutex
l.Lock()
t.Log("acquire write lock")
l.Lock()
t.Log("acquire write lock")
l.Unlock()
l.Unlock()
}
写锁阻止写锁;同样,第二个Lock会阻塞在获取rw.writeSem信号量上,导致两个Lock都无法执行Unlock。
总之一句话,go中RWLock不支持可重入,不要在同一协程调用多次RWMutex的锁,不管读锁还是写锁。
5. 写锁优先级高于读锁
有写锁等待时,优先加写锁,因此写锁不至于饿死一直无法获取到锁。
写操作到来时,会把RWMutex.readerCount值拷贝到RWMutex.readerWait中,用于标记排在写操作前面的读者个数。
前面的读操作结束后,除了会递减RWMutex.readerCount,还会递减RWMutex.readerWait值,当RWMutex.readerWait值变为0时唤醒写操作。
参考资料
go中的sync.RWMutex源码解读
Go 并发实战 -- sync RWMutex
golang sync.RWMutex总结笔记的更多相关文章
- golang sync.RWMutex
sync.RWMutex package main import ( "fmt" "runtime" "sync" ) func click ...
- golang中sync.RWMutex和sync.Mutex区别
golang中sync包实现了两种锁Mutex (互斥锁)和RWMutex(读写锁),其中RWMutex是基于Mutex实现的,只读锁的实现使用类似引用计数器的功能. type Mutex f ...
- golang实现分布式缓存笔记(一)基于http的缓存服务
目录 前言 cache 缓存服务接口 cache包实现 golang http包使用介绍 hello.go Redirect.go http-cache-server 实现 cacheHandler ...
- golang sync包
sync 在golang 文档上,golang不希望通过共享内存来进行进程间的协同操作,而是通过channel的方式来进行,当然,golang也提供了共享内存,锁等机制进行协同操作的包: 互斥锁: M ...
- golang sync.Cond条件变量的使用
cond.Wait()的操作实际上是对与cond绑定的锁先进行解锁,在等待通知:接收到通知后,会尝试加锁,加锁成功则唤醒否则继续等待通知: cond.Waite()前必须对关连锁加锁,否则panic ...
- Golang sync
Go1.9.2 sync库里包含下面几类:Mutex/RWMutex/Cond/WaitGroup/Once/Map/Pool 1.Mutex:互斥锁,等同于linux下的pthread_mutex_ ...
- Go 互斥锁(sync.Mutex)和 读写锁(sync.RWMutex)
什么时候需要用到锁? 当程序中就一个线程的时候,是不需要加锁的,但是通常实际的代码不会只是单线程,所以这个时候就需要用到锁了,那么关于锁的使用场景主要涉及到哪些呢? 多个线程在读相同的数据时 多个线程 ...
- Go语言协程并发---读写锁sync.RWMutex
package main import ( "fmt" "sync" "time" ) /* 读写锁 多路只读 一路只写 读写互斥 */ / ...
- Golang Sync.WaitGroup 使用及原理
Golang Sync.WaitGroup 使用及原理 使用 func main() { var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.A ...
- go源码阅读 - sync/rwmutex
相比于Mutex来说,RWMutex锁的粒度更细,使用RWMutex可以并发读,但是不能并发读写,或者写写. 1. sync.RWMutex的结构 type RWMutex struct { // 互 ...
随机推荐
- 画ERA5气压层剖面图(含地形)
气象上一般使用气压垂直坐标系,在不同的气压层绘制变量.ERA5再分析数据的最低气压层是1000 hPa.实际上,由于地形起伏,一些位置的地面气压低于1000 hPa,一些位置的地面气压高于1000 h ...
- centos6.5升级python3.6并安装boto3模块
1.先升级openssl yum安装各种依赖,yum install gcc gcc-c++ autoconf automake zlib zlib-devel pcre-devel tar zxvf ...
- 94、springboot+minio实现分片上传(超大文件快速上传)
设计由来 在实际的项目开发中常遇到超大附件上传的情况,有时候客户会上传GB大小的文件,如果按照普通的MultipartFile方式来接收上传的文件,那么无疑会把服务器给干崩溃,更别说并发操作了.于是笔 ...
- Oracle11gR2安装
https://blog.csdn.net/newbie_907486852/article/details/80716275
- [ARC140D] One to One
个人思路: 一棵树也只有一个 \(a=-1\) 的点,所以可以把它看做一个点,但是要记录点的大小 \(sz_i\),即为这棵树的大小.如果本来就是一个点,那么大小为 \(1\). 状态:\(dp_{i ...
- 【C学习笔记】【分享】day2-2 不允许创建临时变量,交换两个数的内容(附加题)
加法实现: #include <stdio.h> int main() { int a = 30; int b = 20; a = a + b; b = a - b; a = a - b; ...
- 当FTP不能满足大文件、海量文件传输时,有没有好的替代方案?
很多企业存在大文件.海量文件的传输需求,如涉及到图像数据采集和回传.海量用户数据收集和同步等业务,一般情况,企业还是会采用传统的FTP传输,或者以此为基础,使用脚本或结合其他办公工具来解决传输需求. ...
- 每日一抄 Go语言通信顺序进程简述
package main import ( "fmt" "sync" ) /* Go实现了两种并发形式,第一种是大家普遍认知的多线程共享内存,其实就是 Java ...
- vscode调试openresty
一.快速上手 1.软件下载 官网地址:https://code.visualstudio.com/ 安装视频:https://code.visualstudio.com/docs/getstarted ...
- maven 依赖包冲突解决
maven 查看依赖树结构命令mvn dependency:tree 1.出现下面这样冲突 omitted for duplicate 因重复而省略 2.解决-- 那个项目有问题,先注释掉,在重新一 ...