特别提醒,本文所涉及的源码是go1.21.0 darwin/amd64
文件位置:runtime/slice.go
1 数据结构
1 2 3 4 5 6 7 8
| type RWMutex struct { w Mutex writerSem uint32 readerSem uint32 readerCount atomic.Int32 readerWait atomic.Int32 } const rwmutexMaxReaders = 1 << 30
|
- readerCount:在没有写锁介入的时候,表示的是当前正在读操作的数量。如果有写介入,则
readerCount
+rwmutexMaxReaders
等于正在读操作的数量
- readerWait:在能够获取写锁前,还需要等待多少个读协程释放读锁
2 写锁
1 2 3 4 5 6 7 8 9 10 11 12
| func (rw *RWMutex) Lock() { rw.w.Lock() r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders if r != 0 && rw.readerWait.Add(r) != 0 { runtime_SemacquireRWMutex(&rw.writerSem, false, 0) } }
|
为什么获取写锁的时候要先减去rwmutexMaxReaders,阻塞后续的读操作呢?
主要是防止后续不断的读操作导致写操作被「饿死」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func (rw *RWMutex) Unlock() { r := rw.readerCount.Add(rwmutexMaxReaders) if r >= rwmutexMaxReaders { race.Enable() fatal("sync: Unlock of unlocked RWMutex") } for i := 0; i < int(r); i++ { runtime_Semrelease(&rw.readerSem, false, 0) } rw.w.Unlock() }
|
3 读锁
1 2 3 4 5 6 7
| func (rw *RWMutex) RLock() { if rw.readerCount.Add(1) < 0 { runtime_SemacquireRWMutexR(&rw.readerSem, false, 0) } }
|
1 2 3 4 5 6 7
| func (rw *RWMutex) RUnlock() { if r := rw.readerCount.Add(-1); r < 0 { rw.rUnlockSlow(r) } }
|
1 2 3 4 5 6 7 8 9 10 11
| func (rw *RWMutex) rUnlockSlow(r int32) { if r+1 == 0 || r+1 == -rwmutexMaxReaders { race.Enable() fatal("sync: RUnlock of unlocked RWMutex") } if rw.readerWait.Add(-1) == 0 { runtime_Semrelease(&rw.writerSem, false, 1) } }
|
4 Try
rwmutex.go
文件中除了上面提到的函数,还有两个Try
函数,该函数只在特定场景下使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func (rw *RWMutex) TryRLock() bool { for { c := rw.readerCount.Load() if c < 0 { return false } if rw.readerCount.CompareAndSwap(c, c+1) { return true } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| func (rw *RWMutex) TryLock() bool { if !rw.w.TryLock() { return false } if !rw.readerCount.CompareAndSwap(0, -rwmutexMaxReaders) { rw.w.Unlock() return false } return true }
|