針對Golang 1.9的sync.RWMutex進行分析,與Golang 1.10基本一樣除了將panic
改為了throw
之外其他的都一樣。
RWMutex是讀寫互斥鎖。鎖可以由任意數量的讀取器或單個寫入器來保持。
RWMutex的零值是一個解鎖的互斥鎖。
以下程式碼均去除race競態檢測程式碼
原始碼位置:sync\rwmutex.go
結構體
type RWMutex struct {
w Mutex // 互斥鎖
writerSem uint32 // 寫鎖訊號量
readerSem uint32 // 讀鎖訊號量
readerCount int32 // 讀鎖計數器
readerWait int32 // 獲取寫鎖時需要等待的讀鎖釋放數量
}
複製程式碼
常量
const rwmutexMaxReaders = 1 << 30 // 支援最多2^30個讀鎖
複製程式碼
方法
Lock
提供寫鎖操作.
func (rw *RWMutex) Lock() {
// 競態檢測
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// 使用Mutex鎖
rw.w.Lock()
// Announce to readers there is a pending writer.
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_Semacquire(&rw.writerSem)
}
// 競態檢測
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
race.Acquire(unsafe.Pointer(&rw.writerSem))
}
}
複製程式碼
RLock
提供讀鎖操作,
func (rw *RWMutex) RLock() {
// 競態檢測
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// 每次goroutine獲取讀鎖時,readerCount+1
// 如果寫鎖已經被獲取,那麼readerCount在-rwmutexMaxReaders與0之間,這時掛起獲取讀鎖的goroutine,
// 如果寫鎖沒有被獲取,那麼readerCount>0,獲取讀鎖,不阻塞
// 通過readerCount判斷讀鎖與寫鎖互斥,如果有寫鎖存在就掛起goroutine,多個讀鎖可以並行
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// 將goroutine排到G佇列的後面,掛起goroutine
runtime_Semacquire(&rw.readerSem)
}
// 競態檢測
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}
複製程式碼
RLocker
可以看到RWMutex
實現介面Locker
.
type Locker interface {
Lock()
Unlock()
}
複製程式碼
而方法RLocker
就是將RWMutex
轉換為Locker
.
func (rw *RWMutex) RLocker() Locker {
return (*rlocker)(rw)
}
複製程式碼
總結
讀寫互斥鎖的實現比較有技巧性一些,需要幾點
-
讀鎖不能阻塞讀鎖,引入readerCount實現
-
讀鎖需要阻塞寫鎖,直到所以讀鎖都釋放,引入readerSem實現
-
寫鎖需要阻塞讀鎖,直到所以寫鎖都釋放,引入wirterSem實現
-
寫鎖需要阻塞寫鎖,引入Metux實現