讀寫鎖 RWMutex
寫鎖
加鎖
RWMetex
的寫鎖復用了 Mutex
:
// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (rw *RWMutex) Lock() {if race.Enabled {_ = rw.w.staterace.Disable()}// First, resolve competition with other writers.// writer加鎖rw.w.Lock()// Announce to readers there is a pending writer.// 反轉 readerCount,告訴 reader 有writer競爭鎖// Add(-rwmutexMaxReaders) 將 readerCount 置為負數,表示當前有 writer 競爭鎖r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders// Wait for active readers.// 當前有 reader 持有鎖,writer 等待if r != 0 && rw.readerWait.Add(r) != 0 {runtime_SemacquireRWMutex(&rw.writerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))race.Acquire(unsafe.Pointer(&rw.writerSem))}
}
解鎖
解鎖也是復用的 Mutex
,區別在于解鎖的時候會喚醒所有阻塞的 reader
。
// Unlock unlocks rw for writing. It is a run-time error if rw is
// not locked for writing on entry to Unlock.
//
// As with Mutexes, a locked RWMutex is not associated with a particular
// goroutine. One goroutine may RLock (Lock) a RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {if race.Enabled {_ = rw.w.staterace.Release(unsafe.Pointer(&rw.readerSem))race.Disable()}// Announce to readers there is no active writer.// 反轉 readerCount,告訴 reader 沒有 writer 競爭鎖r := rw.readerCount.Add(rwmutexMaxReaders)if r >= rwmutexMaxReaders {race.Enable()fatal("sync: Unlock of unlocked RWMutex")}// Unblock blocked readers, if any.// 喚醒所有阻塞的 readerfor i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)}// Allow other writers to proceed.// writer 解鎖rw.w.Unlock()if race.Enabled {race.Enable()}
}
讀鎖
加鎖
// RLock locks rw for reading.
//
// It should not be used for recursive read locking; a blocked Lock
// call excludes new readers from acquiring the lock. See the
// documentation on the RWMutex type.
func (rw *RWMutex) RLock() {if race.Enabled {_ = rw.w.staterace.Disable()}// writer 加鎖時,readerCount 是負數// 寫鎖優先級比讀鎖高,reader 休眠// 如果 reader+1 > 0, 當前加了讀鎖if rw.readerCount.Add(1) < 0 {// A writer is pending, wait for it.runtime_SemacquireRWMutexR(&rw.readerSem, false, 0)}if race.Enabled {race.Enable()race.Acquire(unsafe.Pointer(&rw.readerSem))}
}
解鎖
// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {if race.Enabled {_ = rw.w.staterace.ReleaseMerge(unsafe.Pointer(&rw.writerSem))race.Disable()}// 有等待的 writerif r := rw.readerCount.Add(-1); r < 0 {// Outlined slow-path to allow the fast-path to be inlinedrw.rUnlockSlow(r)}if race.Enabled {race.Enable()}
}func (rw *RWMutex) rUnlockSlow(r int32) {if r+1 == 0 || r+1 == -rwmutexMaxReaders {race.Enable()fatal("sync: RUnlock of unlocked RWMutex")}// A writer is pending.// 讀鎖全部解鎖完畢,喚醒 writerif rw.readerWait.Add(-1) == 0 {// The last reader unblocks the writer.runtime_Semrelease(&rw.writerSem, false, 1)}
}
小結
sync.RWMutex
復用sync.Mutex
。- 寫鎖:
- 加鎖時 writer 反轉 readerCount 置為負數,如果當前有 reader 正在執行, readerCount 賦值給 readerWait,writer 休眠。
- 解鎖時 writer 反轉 readerCount 置為正常,并喚醒所有阻塞的 reader。
- 讀鎖:
- 加鎖時 readerCount + 1,如果 readerCount < 0,reader 阻塞。
- 解鎖時 readerCount - 1,如果 readerCount < 0,readerWait - 1,當 readerWait == 0 時,喚醒 writer。
sync.RWMutex
復用 sync.Mutex
。
- 調用 sync.RWMutex.Lock 嘗試獲取寫鎖時;
- 每次 sync.RWMutex.RUnlock 都會將 readerCount 其減一,當它歸零時該 Goroutine 會獲得寫鎖;
- 將 readerCount 減少 rwmutexMaxReaders 個數以阻塞后續的讀操作;
調用 sync.RWMutex.Unlock
釋放寫鎖時,會先通知所有的讀操作,然后才會釋放持有的互斥鎖;
讀寫互斥鎖在互斥鎖之上提供了額外的更細粒度的控制,能夠在讀操作遠遠多于寫操作時提升性能。