RWMutex:是基於Mutex實現的讀寫互斥鎖,一個goroutine能夠持有多個讀鎖或者一個寫鎖,同一時刻只能持有讀鎖或者寫鎖數據結構
數據結構設計:ui
type RWMutex struct { w Mutex // 互斥鎖 writerSem uint32 // 寫鎖信號量 readerSem uint32 // 讀鎖信號量 readerCount int32 // 讀鎖計數器 readerWait int32 // 獲取寫鎖時須要等待的讀鎖釋放數量 }
// 獲取寫鎖 func (rw *RWMutex) Lock() { if race.Enabled { _ = rw.w.state race.Disable() } // 先獲取一把互斥鎖 rw.w.Lock() // 減去最大的讀鎖數量,用0-負數來表示寫鎖已經被獲取 r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders // 設置須要等待釋放的讀鎖數量,若是有,則掛起獲取讀鎖的goroutine 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)) } }
按順序這裏應該介紹釋放寫鎖的代碼了,可是因爲獲取寫鎖中有很重要的幾個邏輯變量,跟獲取讀鎖時強依賴,因此在這裏先說說獲取讀鎖的邏輯atom
// 獲取讀鎖 func (rw *RWMutex) RLock() { if race.Enabled { _ = rw.w.state race.Disable() } // 每次獲取讀鎖時,readerCount+1 // 若是寫鎖已經被獲取,那麼readerCount在-rwmutexMaxReaders與0之間,這時掛起獲取讀鎖的goroutine, // 若是寫鎖沒有被獲取,那麼readerCount>=0,而後就沒而後了 // 這樣經過readerCount的正負就成了讀鎖與寫鎖互斥的判斷條件 if atomic.AddInt32(&rw.readerCount, 1) < 0 { // 掛起,監聽readerSem信號量 runtime_Semacquire(&rw.readerSem) } if race.Enabled { race.Enable() race.Acquire(unsafe.Pointer(&rw.readerSem)) } }
// 釋放讀鎖 func (rw *RWMutex) RUnlock() { if race.Enabled { _ = rw.w.state race.ReleaseMerge(unsafe.Pointer(&rw.writerSem)) race.Disable() } // 讀鎖計數器-1 if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { if r+1 == 0 || r+1 == -rwmutexMaxReaders { race.Enable() panic("sync: RUnlock of unlocked RWMutex") } // 若是獲取寫鎖時的goroutine被阻塞,這時須要獲取讀鎖的goroutine所有都釋放,纔會被喚醒 if atomic.AddInt32(&rw.readerWait, -1) == 0 { // 更新須要釋放的讀鎖數量 // 更新信號量 runtime_Semrelease(&rw.writerSem) } } if race.Enabled { race.Enable() } }
func (rw *RWMutex) Unlock() { if race.Enabled { _ = rw.w.state race.Release(unsafe.Pointer(&rw.readerSem)) race.Release(unsafe.Pointer(&rw.writerSem)) race.Disable() } // 還原加鎖時減去的那一部分readerCount r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) if r >= rwmutexMaxReaders { race.Enable() panic("sync: Unlock of unlocked RWMutex") } // 喚醒獲取讀鎖期間全部被阻塞的goroutine for i := 0; i < int(r); i++ { runtime_Semrelease(&rw.readerSem) } // 釋放互斥鎖資源 rw.w.Unlock() if race.Enabled { race.Enable() } }
總結:spa
讀寫互斥鎖的實現比較有技巧性一些,須要幾點設計
1. 讀鎖不能阻塞讀鎖,引入readerCount實現code
2. 讀鎖須要阻塞寫鎖,直到因此讀鎖都釋放,引入readerSem實現blog
3. 寫鎖須要阻塞讀鎖,直到因此寫鎖都釋放,引入wirterSem實現資源
4. 寫鎖須要阻塞寫鎖,引入Metux實現it