RWMutex 外围还是基于 Mutex 的,如果想理解 Mutex 的话能够看一下我上一篇写的 Mutex 的文章
RWMutex 的个性就是反对并发读。实用于读多写少的场景。
RWMutex 的定义
type RWMutex struct {
w Mutex // 互斥锁
writerSem uint32 // 写锁用的信号量
readerSem uint32 // 读锁用的信号量
readerCount int32 // 以后正在执行读操作的 goroutine 数量
readerWait int32 // 获取写锁时,以后还持有读锁的 goroutine 数量
}
const rwmutexMaxReaders = 1 << 30
RWMutex.Lock()
func (rw *RWMutex) Lock() {
// 首先调用 Mutex 的 Lock 办法获取到锁
rw.w.Lock()
// 把 readerCount 改成正数,这样后续的读操作就会被阻塞
// r 就是以后正在执行读操作的 goroutine 数量
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// 如果以后有正在执行读操作的 goroutine
// 把 r 赋值给 readerWait
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
// 获取写锁的 goroutine 进入休眠,期待被唤醒
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
}
RWMutex.Unlock()
func (rw *RWMutex) Unlock() {
// 把 readerCount 改成负数,这样后续读操作就不会被阻塞了
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
...
// 手动唤醒之前被写锁阻塞的读操作 goroutine
for i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)
}
// 开释互斥锁,其余写锁就能够竞争互斥锁了
rw.w.Unlock()}
RWMutex.RLock()
func (rw *RWMutex) RLock() {
...
// readerCount + 1
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// 小于 0,阐明有其余 goroutine 获取了写锁, 以后 goroutine 期待
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
...
}
RWMutex.RUnlock()
func (rw *RWMutex) RUnlock() {
...
// readerCount - 1
// readerCount < 0, 阐明其余 gouroutine 获取了写锁,正在期待还持有读锁的 goroutine 开释读锁
// readerCount >= 0, 阐明没有写锁被阻塞,间接返回就行了
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
// 开释读锁
rw.rUnlockSlow(r)
}
...
}
func (rw *RWMutex) rUnlockSlow(r int32) {
...
// readerWait - 1
// 判断以后 goroutine 是不是最初一个开释读锁
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
// 唤醒写锁
runtime_Semrelease(&rw.writerSem, false, 1)
}
}
总结
获取读锁的流程
- readerCount + 1
- 以 readerCount<0, 判断是否被写锁阻塞,是的话,以后 goroutine 进入休眠
开释读锁的流程
- readerCount – 1
- 以 readerCount<0, 判断是否有写锁
- 没有写锁的话,间接返回
- 有写锁的话,调用 rUnlockSlow 办法,readerWait – 1
- 如果 readerWait == 0, 阐明以后 goroutine 是写锁期待的最初一个读锁 goroutine,须要唤醒写锁 goroutine
获取写锁的流程
- 先获取互斥锁
- readerCount – rwmutexMaxReaders,后续读操作全副阻塞
- readerWait += readerCount,把以后正在执行读操作的数量加到 readerWait 上
- 如果 readerWait != 0,阐明以后还有其余 goroutine 持有读锁,以后 goroutine 进入睡眠,期待唤醒
开释写锁流程
- readerCount + rwmutexMaxReaders, 后续读锁不会阻塞
- readerCount 代表之前被写锁阻塞的读锁 goroutine 个数,唤醒 readerCount 个读锁 goroutine
- 最初开释互斥锁
最初
RWMutex 绝对 Mutex,减少了读锁的管制,就代码逻辑复杂度而言,RWMutex 比 Mutex 要简略很多,对 Mutex 的流程相熟的话,很快就能把握 RWMutex 的原理