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的原理