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, 如果结果小于零,说明:

  1. 原来 readerCount == 0 || readerCount == -rwmutexMaxReaders, 对未加锁的对象执行了解锁操作
  2. 原来 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源码分析相关推荐

  1. go sync.map 源码分析

    一 简言 1.1 文件所在目录:go/src/sync/map.go 1.2 本篇博客的go版本:go1.10.3 二 原理说明 空间换时间.通过冗余的两个数据结构(read.dirty),实现加锁对 ...

  2. go sync.WaitGroup源码分析

    go版本 :1.10.3 原理实现:信号量 信号量是Unix系统提供的一种保护共享资源的机制,用于防止多个线程同时访问某个资源. 可简单理解为信号量为一个数值: 当信号量>0时,表示资源可用,获 ...

  3. 【Go】sync.WaitGroup 源码分析

    WaitGroup sync.WaitGroup 用于等待一组 goroutine 返回,如: var wg = sync.WaitGroup{}func do() {time.Sleep(time. ...

  4. Golang sync.Mutex源码分析

    sync.Mutex是一个不可重入的排他锁. 这点和Java不同,golang里面的排它锁是不可重入的.当一个 goroutine 获得了这个锁的拥有权后, 其它请求锁的 goroutine 就会阻塞 ...

  5. Docker源码分析(三):Docker Daemon启动

    http://www.infoq.com/cn/articles/docker-source-code-analysis-part3 1 前言 Docker诞生以来,便引领了轻量级虚拟化容器领域的技术 ...

  6. Istio Pilot 源码分析(二)

    张海东, ‍多点生活(成都)云原生开发工程师. 本篇主要介绍 Pilot 源码中的 ServiceEntryStore 及其推送 xDS 的流程. 本文为 Istio Pilot 源码分析系列的第二篇 ...

  7. 【Golang 源码】sync.Map 源码详解

    sync.Map 不安全的 map go 中原生的 map 不是并发安全的,多个 goroutine 并发地去操作一个 map 会抛出一个 panic package main import &quo ...

  8. 以太坊POA共识机制Clique源码分析

    以太坊中除了基于运算能力的POW(Ethash)外,还有基于权利证明的POA共识机制,Clique是以太坊的POA共识算法的实现,这里主要对POA的Clique相关源码做一个解读分析. Clique的 ...

  9. kube-controller-manager源码分析(三)之 Informer机制

    本文个人博客地址:https://www.huweihuang.com/kubernetes-notes/code-analysis/kube-controller-manager/sharedInd ...

最新文章

  1. 一个有趣的python排序模块:bisect
  2. yii2的Console定时任务创建
  3. 结构体前面加星号_C语言中带星号的类型指针有哪些特性
  4. 10.傅里叶变换——傅里叶变换、计算傅里叶变换_3
  5. git完全cli指南之详细思维导图整理分享
  6. pytorch读取数据集(分类文件夹加载)—ImageFolder()
  7. 计算机的音量找不到了,win7旗舰版64位系统右下角音量小喇叭图标不见了怎么找回...
  8. Next数组个人理解
  9. 手机app邮箱如何和学校邮箱自动同步
  10. Win7怎么进入安全模式改密码
  11. 2015阿里实习生面试
  12. IIS 支持 flv文件播放 (浏览器中可打开一flv文件)
  13. 浅谈git rebase和git checkout --ours(theirs)
  14. jenkins 删除 构建历史
  15. 新用户购买阿里云服务器 阿里云搭建Csapp Lab环境
  16. vivo前端智能化实践:机器学习在自动网页布局中的应用
  17. 2.4模拟打牌游戏中的发牌过程
  18. 【软件工程师学硬件】之 振荡器(2)
  19. Ubuntu下快速关机的方法
  20. 汉邦高科硬盘录像机手机远程客户端软件 安卓系统

热门文章

  1. #python计算结果百位500向下取整,(0-499取000,500-999取500)
  2. 在程序开发中日志级别
  3. POJ 1273 Drainage Ditches 最大流
  4. SqlServer 的IDENTITY_INSERT设置为OFF问题
  5. IOS-UITextField类
  6. 接口,new,匿名内部类
  7. 给你这张图,你能搜索到来历吗
  8. CentOS7查看开放端口命令
  9. es6中class类的全方面理解(三)------静态方法
  10. Haproxy+Percona-XtraDB-Cluster 集群