相比于Mutex来说,RWMutex锁的粒度更细,使用RWMutex可以并发读,但是不能并发读写,或者写写。

1. sync.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
// 当前执行读的goroutine的数量
readerCount int32 // number of pending readers
// 获取写锁时需要等待的读锁释放数量
readerWait int32 // number of departing readers
} const rwmutexMaxReaders = 1 << 30

  

2. 加读锁 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()
}
// 调用这个原子方法,原子加 1
// 如果写锁已经被获取,那么 readercount在 -rwmutexMaxReaders和0之间,这是将请求获取 RLock的 goroutine的挂起
// 如果写锁没有被获取,那么 readercount大于 0,获取读锁 ,不阻塞。
// 通过 readerCount判断读锁与写锁互斥,如果存在写锁则挂起goroutine,多个读锁可以并行
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
// 这时休眠这个goroutine,等待被唤醒
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}

3. 解读锁

// 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()
}
// 正在读的goroutine数减1
// 如果小于0,说明 1. 当前有正在等待获取写锁的goroutine或者多次解锁,进入慢速通道
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
// Outlined slow-path to allow the fast-path to be inlined
rw.rUnlockSlow(r)
}
if race.Enabled {
race.Enable()
}
}

func (rw *RWMutex) rUnlockSlow(r int32) {
// 如果多次解锁,则抛出异常
if r+1 == 0 || r+1 == -rwmutexMaxReaders {
race.Enable()
throw("sync: RUnlock of unlocked RWMutex")
}
// A writer is pending.
// 如果有正在等待获取写锁的goroutine, 并且当前的read goroutine是最后一个持有读锁的goroutine, 那么通过信号量唤醒等待获取写锁的goroutine.
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
// The last reader unblocks the writer.
runtime_Semrelease(&rw.writerSem, false, 1)
}
}

  

4. 加写锁

// 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.
// 首先调用互斥锁的 Lock,获取到互斥锁
rw.w.Lock()
// Announce to readers there is a pending writer.
// 阻塞后续的读操作
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
// 如果计算之后仍有其他goroutine获取读锁,,那么调用 runtime_SemacquireMutex 休眠当前的goroutine直到所有的读操作完成
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
race.Acquire(unsafe.Pointer(&rw.writerSem))
}
}

5. 解写锁

// 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.
// 将 readerCount 恢复
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
race.Enable()
throw("sync: Unlock of unlocked RWMutex")
}
// Unblock blocked readers, if any.
// 循环唤醒等待获取读锁的 goroutine
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
// Allow other writers to proceed.
rw.w.Unlock()
if race.Enabled {
race.Enable()
}
}

  

go源码阅读 - sync/rwmutex的更多相关文章

  1. go源码阅读 - sync/mutex

    Mutex是go标准库中的互斥锁,用于处理并发场景下共享资源的访问冲突问题. 1. Mutex定义: // A Mutex is a mutual exclusion lock. // The zer ...

  2. 10 DelayQueue 延时队列类——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 www.cnblogs.com/oloroso/ 本文由乌合 ...

  3. Netty源码阅读(一) ServerBootstrap启动

    Netty源码阅读(一) ServerBootstrap启动 转自我的Github Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速 ...

  4. 【 js 基础 】【 源码学习 】backbone 源码阅读(一)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  5. 【 js 基础 】【 源码学习 】backbone 源码阅读(三)浅谈 REST 和 CRUD

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  6. InfluxDB源码阅读之snapshotter服务

    操作系统 : CentOS7.3.1611_x64 go语言版本:1.8.3 linux/amd64 InfluxDB版本:1.1.0 服务模块介绍 源码路径: github.com/influxda ...

  7. Apollo源码阅读笔记(二)

    Apollo源码阅读笔记(二) 前面 分析了apollo配置设置到Spring的environment的过程,此文继续PropertySourcesProcessor.postProcessBeanF ...

  8. 【 js 基础 】【 源码学习 】backbone 源码阅读(三)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  9. Redis源码阅读(二)高可用设计——复制

    Redis源码阅读(二)高可用设计-复制 复制的概念:Redis的复制简单理解就是一个Redis服务器从另一台Redis服务器复制所有的Redis数据库数据,能保持两台Redis服务器的数据库数据一致 ...

随机推荐

  1. 旅游清单一步搭建,Angular助力你的踏春计划

    春天的脚步愈发临近,相信很多小伙伴已经开始规划自己的踏春计划了,无论是欣赏名胜古迹,还是走访风土人文,你都需要提前准备一份旅游清单!有了这款Angular旅游计划应用,从地点到预算,它都能帮助你创建自 ...

  2. rancher接管已部署的集群

    1.选择一台服务器部署rancher服务 docker pull rancher/rancher:v2.5.6 #拉取rancher镜像 docker run --privileged -d -v / ...

  3. 6月25日 Django 分页 cookie、session

    cookie Cookie的由来 大家都知道HTTP协议是无状态的. 无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不 ...

  4. 6月11日 python复习 mysql

    01. 列举常见的关系型数据库和非关系型都有那些? 1.关系型数据库通过外键关联来建立表与表之间的关系,---------常见的有:SQLite.Oracle.mysql 2.非关系型数据库通常指数据 ...

  5. Mybatis 的 Xml 映射文件中,不同的 Xml 映射文件,id 是否可以重复?

    不同的 Xml 映射文件,如果配置了 namespace,那么 id 可以重复:如果没有配 置 namespace,那么 id 不能重复: 原因就是 namespace+id 是作为 Map<S ...

  6. 在java web工程中jsp页面中使用kindeditor

    在这之前我们用Notepad++写过kindeditor 在Java web工程里也差不多 首先我们复制之前的thml代码粘贴到工程里 然后把样式也复制进去 然后就可以运行了

  7. scrapy --爬取媒体文件示例详解

    scrapy 图片数据的爬取 基于scrapy进行图片数据的爬取: 在爬虫文件中只需要解析提取出图片地址,然后将地址提交给管道 配置文件中写入文件存储位置:IMAGES_STORE = './imgs ...

  8. 学习 Haproxy (一)

    haproxy是一个开源的.高性能的基于tcp和http应用代理的HA的.LB服务软件,它支持双机热备.HA.LB.虚拟主机.图形界面查看状态信息等功能,其配置简单.维护方便,而且后端RS的healt ...

  9. js技术之截取substring(int,int)

    案例:把所有单词以空格为分割并将首字母转为大写 <!DOCTYPE html><html lang="en"><head> <meta c ...

  10. 顺利通过EMC实验(1)