【Go】sync.RWMutex源码分析
RWMutex
读写锁相较于互斥锁有更低的粒度,它允许并发读,因此在读操作明显多于写操作的场景下能减少锁竞争的次数,提高程序效率。
type RWMutex struct {w Mutex // held if there are pending writerswriterSem uint32 // semaphore for writers to wait for completing readersreaderSem uint32 // semaphore for readers to wait for completing writersreaderCount int32 // number of pending readersreaderWait int32 // number of departing readers
}
RWMutex
结构体中包含五个字段,分别表示:
w
: 复用互斥锁writerSem 和 readerSem
: 用于写等待读和读等待写的信号量readerCount
:readerWait
: 等待写锁释放的读者数量
读锁
RLock
当有 goroutine 写时,是不允许读的,这时会把 readerCount
设置为负,这时读 goroutine 应该被阻塞
func (rw *RWMutex) RLock() {if atomic.AddInt32(&rw.readerCount, 1) < 0 {// 阻塞读runtime_SemacquireMutex(&rw.readerSem, false, 0)}
}
RUnlock
读锁解锁时只需要将 readerCount - 1
, 如果结果小于零,说明:
- 原来
readerCount == 0 || readerCount == -rwmutexMaxReaders
, 对未加锁的对象执行了解锁操作 - 原来
readerCount < 0
, 有正在执行的写操作
func (rw *RWMutex) RUnlock() {if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {// Outlined slow-path to allow the fast-path to be inlinedrw.rUnlockSlow(r)}
}func (rw *RWMutex) rUnlockSlow(r int32) {if r+1 == 0 || r+1 == -rwmutexMaxReaders {race.Enable()throw("sync: RUnlock of unlocked RWMutex")}// 所有读操作结束后,触发写的写信号量if atomic.AddInt32(&rw.readerWait, -1) == 0 {// The last reader unblocks the writer.runtime_Semrelease(&rw.writerSem, false, 1)}
}
写锁
Lock
func (rw *RWMutex) Lock() {// 获取互斥写锁rw.w.Lock()// 阻塞读r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders// 如果有人在读,需要等待读锁释放if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {// 阻塞等待读锁释放runtime_SemacquireMutex(&rw.writerSem, false, 0)}
}
Lock 会先通过互斥锁阻塞写操作,然后禁止读锁获取,等待已经持有读锁的 goroutine 释放读锁,这样可以避免连续的写操作使读陷入饥饿。
Unlock
func (rw *RWMutex) Unlock() {// 重新允许读锁获取r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)if r >= rwmutexMaxReaders {race.Enable()throw("sync: Unlock of unlocked RWMutex")}// 触发等待中的写锁的信号量for i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)}// 互斥锁解锁rw.w.Unlock()
}
总结
在极端情况下:
- 如果完全没有写,读锁加锁只是将 readerCount 加一,解锁只是将其减一,不存在锁竞争。
- 如果只有写,加锁和解锁都比互斥锁多了一个对 readerCount 取反操作
在一般情况下,读锁在获取锁前会检查 readerCount, 如果为负,说明有人在写,则进入阻塞状态,等待 readerSem
的信号,写锁获取锁在得到互斥锁后会先设置 readerCount 为负,阻止新的读者获取读锁,然后需要等待所有已经持有读锁的 goroutine 释放读锁,这里使用的是 readerWait
,当 readerCount 为负时,如果读锁被释放,该量就会减一,当 readerWait == 0
时,则说明所有在写锁获取之前获得的读锁都被释放了,最后一个释放的读锁会通过 writerSem
通知写对象。
写锁释放时,需要通过 readerSem
信号触发所有阻塞中的写对象。
【Go】sync.RWMutex源码分析相关推荐
- go sync.map 源码分析
一 简言 1.1 文件所在目录:go/src/sync/map.go 1.2 本篇博客的go版本:go1.10.3 二 原理说明 空间换时间.通过冗余的两个数据结构(read.dirty),实现加锁对 ...
- go sync.WaitGroup源码分析
go版本 :1.10.3 原理实现:信号量 信号量是Unix系统提供的一种保护共享资源的机制,用于防止多个线程同时访问某个资源. 可简单理解为信号量为一个数值: 当信号量>0时,表示资源可用,获 ...
- 【Go】sync.WaitGroup 源码分析
WaitGroup sync.WaitGroup 用于等待一组 goroutine 返回,如: var wg = sync.WaitGroup{}func do() {time.Sleep(time. ...
- Golang sync.Mutex源码分析
sync.Mutex是一个不可重入的排他锁. 这点和Java不同,golang里面的排它锁是不可重入的.当一个 goroutine 获得了这个锁的拥有权后, 其它请求锁的 goroutine 就会阻塞 ...
- Docker源码分析(三):Docker Daemon启动
http://www.infoq.com/cn/articles/docker-source-code-analysis-part3 1 前言 Docker诞生以来,便引领了轻量级虚拟化容器领域的技术 ...
- Istio Pilot 源码分析(二)
张海东, 多点生活(成都)云原生开发工程师. 本篇主要介绍 Pilot 源码中的 ServiceEntryStore 及其推送 xDS 的流程. 本文为 Istio Pilot 源码分析系列的第二篇 ...
- 【Golang 源码】sync.Map 源码详解
sync.Map 不安全的 map go 中原生的 map 不是并发安全的,多个 goroutine 并发地去操作一个 map 会抛出一个 panic package main import &quo ...
- 以太坊POA共识机制Clique源码分析
以太坊中除了基于运算能力的POW(Ethash)外,还有基于权利证明的POA共识机制,Clique是以太坊的POA共识算法的实现,这里主要对POA的Clique相关源码做一个解读分析. Clique的 ...
- kube-controller-manager源码分析(三)之 Informer机制
本文个人博客地址:https://www.huweihuang.com/kubernetes-notes/code-analysis/kube-controller-manager/sharedInd ...
最新文章
- 一个有趣的python排序模块:bisect
- yii2的Console定时任务创建
- 结构体前面加星号_C语言中带星号的类型指针有哪些特性
- 10.傅里叶变换——傅里叶变换、计算傅里叶变换_3
- git完全cli指南之详细思维导图整理分享
- pytorch读取数据集(分类文件夹加载)—ImageFolder()
- 计算机的音量找不到了,win7旗舰版64位系统右下角音量小喇叭图标不见了怎么找回...
- Next数组个人理解
- 手机app邮箱如何和学校邮箱自动同步
- Win7怎么进入安全模式改密码
- 2015阿里实习生面试
- IIS 支持 flv文件播放 (浏览器中可打开一flv文件)
- 浅谈git rebase和git checkout --ours(theirs)
- jenkins 删除 构建历史
- 新用户购买阿里云服务器 阿里云搭建Csapp Lab环境
- vivo前端智能化实践:机器学习在自动网页布局中的应用
- 2.4模拟打牌游戏中的发牌过程
- 【软件工程师学硬件】之 振荡器(2)
- Ubuntu下快速关机的方法
- 汉邦高科硬盘录像机手机远程客户端软件 安卓系统