談談自己對 GO 的 RWMutex 的理解
【導讀】go 中 RWMutex 和 Mutex 的區別在哪裏?什麼場景下使用哪種鎖?本文做了詳細介紹。
RWMutex 核心還是基於 Mutex 的,RWMutex 的特性就是支持併發讀。適用於讀多寫少的場景。
RWMutex 的定義
type RWMutex struct {
w Mutex // 互斥鎖
writerSem uint32 // 寫鎖用的信號量
readerSem uint32 // 讀鎖用的信號量
readerCount int32 // 當前正在執行讀操作的 goroutine 數量
readerWait int32 // 獲取寫鎖時,當前還持有讀鎖的 goroutine 數量
}
const rwmutexMaxReaders = 1 << 30
RWMutex.Lock()
func (rw *RWMutex) Lock() {
// 首先調用 Mutex 的 Lock 方法獲取到鎖
rw.w.Lock()
// 把 readerCount 改成負數,這樣後續的讀操作就會被阻塞
// r 就是當前正在執行讀操作的 goroutine 數量
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// 如果當前有正在執行讀操作的 goroutine
// 把 r 賦值給 readerWait
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
// 獲取寫鎖的 goroutine 進入休眠,等待被喚醒
runtime_SemacquireMutex(&rw.writerSem, false, 0)
}
}
RWMutex.Unlock()
func (rw *RWMutex) Unlock() {
// 把 readerCount 改成正數,這樣後續讀操作就不會被阻塞了
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
...
// 手動喚醒之前被寫鎖阻塞的讀操作 goroutine
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false, 0)
}
// 釋放互斥鎖,其他寫鎖就可以競爭互斥鎖了
rw.w.Unlock()
}
RWMutex.RLock()
func (rw *RWMutex) RLock() {
...
// readerCount + 1
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// 小於 0,說明有其他 goroutine 獲取了寫鎖,當前 goroutine 等待
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
...
}
RWMutex.RUnlock()
func (rw *RWMutex) RUnlock() {
...
// readerCount - 1
// readerCount < 0, 說明其他 gouroutine 獲取了寫鎖,正在等待還持有讀鎖的 goroutine 釋放讀鎖
// readerCount >= 0, 說明沒有寫鎖被阻塞,直接返回就行了
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
// 釋放讀鎖
rw.rUnlockSlow(r)
}
...
}
func (rw *RWMutex) rUnlockSlow(r int32) {
...
// readerWait - 1
// 判斷當前 goroutine 是不是最後一個釋放讀鎖
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
// 喚醒寫鎖
runtime_Semrelease(&rw.writerSem, false, 1)
}
}
總結
獲取讀鎖的流程
-
readerCount + 1
-
以 readerCount<0, 判斷是否被寫鎖阻塞,是的話,當前 goroutine 進入休眠
釋放讀鎖的流程
-
readerCount - 1
-
以 readerCount<0, 判斷是否有寫鎖
-
沒有寫鎖的話,直接返回
-
有寫鎖的話,調用 rUnlockSlow 方法,readerWait - 1
-
如果 readerWait == 0, 說明當前 goroutine 是寫鎖等待的最後一個讀鎖 goroutine,需要喚醒寫鎖 goroutine
獲取寫鎖的流程
-
先獲取互斥鎖
-
readerCount - rwmutexMaxReaders,後續讀操作全部阻塞
-
readerWait += readerCount,把當前正在執行讀操作的數量加到 readerWait 上
-
如果 readerWait != 0 ,說明當前還有其他 goroutine 持有讀鎖,當前 goroutine 進入睡眠,等待喚醒
釋放寫鎖流程
-
readerCount + rwmutexMaxReaders, 後續讀鎖不會阻塞
-
readerCount 代表之前被寫鎖阻塞的讀鎖 goroutine 個數,喚醒 readerCount 個讀鎖 goroutine
-
最後釋放互斥鎖
最後
RWMutex 相對 Mutex,增加了讀鎖的控制,就代碼邏輯複雜度而言,RWMutex 比 Mutex 要簡單很多,對 Mutex 的流程熟悉的話,很快就能掌握 RWMutex 的原理
轉自:iuoui
segmentfault.com/a/1190000023372322
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/VsV_8urVXxqRH3ul_ROh0A