读写锁特点
- 读+读:通过
- 读+写:阻塞
- 写+读:阻塞
- 写+写:阻塞
type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // 控制获取写锁的信号量
readerSem uint32 // 控制获取读锁的信号量
readerCount int32
readerWait int32
}
func (rw *RWMutex) RLock() {
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
}
func (rw *RWMutex) RUnlock() {
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")
}
// 每次释放读锁,readerWait 都会减1,直到为0就释放 writerSem 信号量
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
runtime_Semrelease(&rw.writerSem, false, 1)
}
}
func (rw *RWMutex) Lock() {
rw.w.Lock()
// r 就是 readerCount 原先的值
// readerCount 减去一个很大的值,就是表明写锁已经加上了,后续的读锁请求将阻塞
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// 获取了写锁,还要等待所以的读锁全部释放
// readerWait + r 其实就是还有多少个读锁没有释放,
// 如果不为0,则需要等待信号量
// 如果为0,则直接成功,不能等待信号量
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
}
func (rw *RWMutex) Unlock() {
// 将 readerCount 恢复正常值,也就是读锁的个数
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
throw("sync: Unlock of unlocked RWMutex")
}
// 向所以的读锁释放信号量
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
rw.w.Unlock()
}
首先是获取读锁和释放读锁,在不考虑其他的情况下,直接加减readerCount
即可。
获取写锁的过程是互斥的,因此需要一个互斥锁来加持;拿到了互斥锁之后,就意味着抢到了写锁,如果此时已经有协程在使用读锁,那么需要等到这些读锁全部释放,对应的信号量为writerSem
。
读写锁的关键是,既要操作信号量,但是有可能根本就没有对方,所以又不能盲目的操作,所以先判断一个数量,然后再操作。