相比于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. Asp.net Core Filter过滤器异常处理

    本文旨在: 1 继承ExceptionFilterAttribute,重写Override OnException(ExceptionContext context)处理异常 2 在.netCore中 ...

  2. python3判断一个数是否为素数

    while True: num = int(input('请输入一个数:')) for i in range(2,num):#判断在num之前的数能不能把num整除 if(num%i == 0): p ...

  3. Linux中8个有用的touch命令

    在Linux中,每个文件都有时间戳,并且每个文件都存储上次访问时间,上次修改时间,上次改变时间的信息.因此,无论什么时候我们创建新的文件,访问或者修改一个存在的文件,那个文件的时间戳会自动被更改. 在 ...

  4. 4月8日 python学习总结 模块与包

    一.包 #官网解释 Packages are a way of structuring Python's module namespace by using "dotted module n ...

  5. Python库国内镜像

    中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/ http://pypi.mirrors.ustc.edu.cn/simple/ 豆瓣 http://py ...

  6. 一道有意思的 CSS 面试题,FizzBu​​zz ~

    FizzBu​​zz 是一道很有意思的题目.我们来看看题目: 如果遇见了 3 的倍数要说 Fizz,5 的倍数就说 Buzz,如果即是 3 的倍数又是 5 的倍数就说 FizzBuzz. 如果是在一些 ...

  7. Java9至17的新特性总结

    总览 讲讲Java 9-17 的一些语法糖和一些新发布的jeps, 重点讲讲JVM的垃圾回收器 时间线 SpringBoot 为什么选择Java17这个版本.我估计跟下面这个图有关系. Java 8 ...

  8. 【Mybatis】SQL语句的解析执行过程原理

    sqlSession简单介绍 拿到SqlSessionFactory对象后,会调用SqlSessionFactory的openSesison方法,这个方法会创建一个Sql执行器(Executor),这 ...

  9. Sting -- byte[]互转

    1.String -->byte[] String str = "中国"; byte[] bys = str.getBytes(); Arrays.toString(bys) ...

  10. 创建自定义ClassLoader,绕过双亲委派

    1.什么是类加载 通过javac将.java文件编译成.class字节码文件后,则需要将.class加载到JVM中运行,哪么是谁将.class加载到JVM的呢?那就是类加载器啦. 2.类加载器类型 B ...