相比于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()
}
}

  

最新文章

  1. Logback配置连接
  2. 详解ASP.NET MVC的请求生命周期
  3. 删除配置文件解决OS X各种WiFi无法连接的顽固问题,解决MAC无法连接wif的情况 Preferences
  4. IOS开发--UI进阶之iCarousel学习(待翻译)
  5. 第7章 使用RAID与LVM磁盘阵列技术
  6. mysql 性能问题
  7. PowerShell 语法结构
  8. nbIoT基础概念
  9. gc 辅助打印信息
  10. php 文件上传简单类---限制仅上传jpg文件
  11. UVa 557 (概率 递推) Burger
  12. php 生成正态分布随机数
  13. gulp 前端自动化工具
  14. php笔记(六)PHP类与对象之对象接口
  15. SpringMVC强大的数据绑定(2)——第六章 注解式控制器详解
  16. OpenResty 执行阶段的概念和用途
  17. Python--day12(三元表达式、函数对象、名称空间与作用域、函数嵌套定义)
  18. SQLServer 中多行数据合并成一行数据(一个字段)
  19. 两个同级div重叠的情况
  20. centos7.4下的KVM虚拟机安装使用

热门文章

  1. table元素使用bug
  2. vue学习过程总结(07) - vue的后台服务API封装及跨域问题的解决
  3. CVE-2015-1427(Groovy 沙盒绕过 && 代码执行漏洞)
  4. python中文及符号检测工具带GUI界面
  5. [转载]SQL注入绕过WAF的方法总结
  6. 专访 KubeVela 核心团队:如何简化云原生复杂环境下的应用交付和管理
  7. 一文带你了解Lakehouse的并发控制:我们是否过于乐观
  8. cornerstone 忽略不必要文件
  9. 在并发情况下,Elasticsearch 如果保证读写一致?
  10. 列举 Spring Framework 的优点?