go RWMutex源码分析
适用场景
并发场景下读多写少。
字段含义
RWMutex基于Mutex,写优先,Lock函数(反转readerCount)会阻止新的reader获取锁。
type RWMutex struct {
w Mutex // writer之间互斥
writerSem uint32 // writer信号量
readerSem uint32 // reader信号量
readerCount int32 // reader数量
readerWait int32 // writer等待reader的数量
}
// reader最大数量
const rwmutexMaxReaders = 1 << 30
RLock
func (rw *RWMutex) RLock() {
// 如果readerCount是负值,那么有writer请求锁时,阻塞后来的reader
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
}
RUnlock
func (rw *RWMutex) RUnlock() {
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
// 有等待的writer
rw.rUnlockSlow(r)
}
}
func (rw *RWMutex) rUnlockSlow(r int32) {
// 最后一个reader释放锁后唤醒writer
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
runtime_Semrelease(&rw.writerSem, false, 1)
}
}
Lock
func (rw *RWMutex) Lock() {
rw.w.Lock()
// 反转readerCount,告诉reader有writer竞争锁,阻塞后面的reader
// readerCount = readerWait(writer前面的读者数量)+writer后面的读者数量
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// 如果当前有reader持有锁,那么等待前面的reader释放锁
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
}
Unlock
func (rw *RWMutex) Unlock() {
// 反转readerCount,新来的reader可以直接获取到读锁
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
// 唤醒writer后所有reader
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
rw.w.Unlock()
}
死锁—加读锁->加写锁->加读锁
package main
import (
"sync"
"time"
)
func main() {
m := sync.RWMutex{}
m.RLock()
go func() {
m.Lock()
}()
time.Sleep(1 * time.Second)
m.RLock()
}
Go的读写锁没有记录持有锁的协程信息,不支持重入和降级。
Java的ReentantReadWriteLock支持降级(持有写锁的线程可继续获取到读锁)和可重入。