Go标准库中的RWMutex是基于Mutex实现的。RWMutex包含一个Mutex,以及四个辅助字段。
type RWMutex struct {
w Mutex // 互斥锁解决多个writer的竞争
writerSem uint32 // writer信号量
readerSem uint32 // reader信号量
readerCount atomic.Int32 // reader的数量
readerWait atomic.Int32 // writer等待完成的reader的数量
}
const rwmutexMaxReaders = 1 << 30
字段 w:为 writer 的竞争锁而设计;
字段 readerCount:记录当前 reader 的数量(以及是否有 writer 竞争锁);
readerWait:记录 writer 请求锁时需要等待 read 完成的 reader 的数量;
writerSem 和 readerSem:都是为了阻塞设计的信号量。
常量 rwmutexMaxReaders,定义了最大的 reader 数量。
读锁
func (rw *RWMutex) RLock() {
...
if rw.readerCount.Add(1) < 0 {
runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)
}
...
}
func (rw *RWMutex) RUnlock() {
...
if r := rw.readerCount.Add(-1); r < 0 {
rw.rUnlockSlow(r)
}
...
}
func (rw *RWMutex) rUnlockSlow(r int32) {
...
if rw.readerWait.Add(-1) == 0 {
runtime_Semrelease(&rw.writerSem, false, 1)
}
}
逻辑:加锁时,将读的数量+1,并判断+1后的数量是否为负值,为负时说明有写操作在等待,然后让读操作休眠;解锁时,读的数量-1,判断是否有写等待,有就将readerWait-1,若当前读的为最后一个,则唤醒写锁。
写锁
func (rw *RWMutex) Lock() {
...
rw.w.Lock()
// Announce to readers there is a pending writer.
r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders
if r != 0 && rw.readerWait.Add(r) != 0 {
runtime_SemacquireRWMutex(&rw.writerSem, false, 0)
}
...
}
func (rw *RWMutex) Unlock() {
...
r := rw.readerCount.Add(rwmutexMaxReaders)
...
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
rw.w.Unlock()
...
}
逻辑:加锁时,加互斥锁,反转readCount,让使用读锁的时候,知道有写锁在竞争,并将当前读的数量加到readerWait上,让写锁休眠;解锁时,先将readerCount转为正数,再唤醒所有的读操作,最后解开互斥锁。
注意:
- 锁不能复制,
- 重入导致死锁
- 连续调用写锁
- 读锁中调用了写锁
- 形成环形依赖
- 未释放加锁的RWMutex