RWMutex 为读写锁,适用于读多写少的场景,可以同时有多个goroutine对临界区加读锁,此时写操作的goroutine被阻塞;同一时刻仅能有一个goroutine获得写锁,此时其他写操作和读操作的goroutine被阻塞。
一、数据结构
type RWMutex struct {
w Mutex // 互斥锁
writerSem uint32 // 写信号量
readerSem uint32 // 读信号量
readerCount int32 // 读锁计数器
readerWait int32 // 加写锁前需要等待释放的读锁数量
}
二、使用方法
1. RLock:加读锁
func (rw *RWMutex) RLock() {
// 获取读锁后,读锁计数器+1
// 若readerCount+1后小于0,意味着有写锁被获取,此时读操作不能进行被挂起
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
}
2. RUnlock:解读锁
func (rw *RWMutex) RUnlock() {
// 读锁计数器-1,并根据返回的值进行下一步操作
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {,
rw.rUnlockSlow(r)
}
}
func (rw *RWMutex) rUnlockSlow(r int32) {
// 第一种情况为上锁前先解锁,第二种情况为先上写锁再解读锁,均抛出异常
if r+1 == 0 || r+1 == -rwmutexMaxReaders {
throw("sync: RUnlock of unlocked RWMutex")
}
// 读锁释放完毕后,通知写锁
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
runtime_Semrelease(&rw.writerSem, false, 1)
}
}
3. Lock:加写锁
func (rw *RWMutex) Lock() {
// 上锁
rw.w.Lock()
// 将readerCount设为负数,表明有写操作在等待
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// r != 0即此时还有读操作在进行,则挂起当前goroutine等待读锁释放
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
}
4. Unlock:解写锁
func (rw *RWMutex) Unlock() {
// 通知表明此时读锁可被获取
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
// 未加写锁而解写锁,抛出异常
if r >= rwmutexMaxReaders {
throw("sync: Unlock of unlocked RWMutex")
}
// 唤醒等待读锁的goroutine
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
// 解锁,此时其他goroutine可获得写锁
rw.w.Unlock()
}