談談本身對GO的RWMutex的理解

RWMutex核心仍是基於Mutex的,若是想了解Mutex的話能夠看一下我上一篇寫的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)
    }
}

總結

獲取讀鎖的流程
  1. readerCount + 1
  2. 以readerCount<0,判斷是否被寫鎖阻塞,是的話,當前goroutine進入休眠
釋放讀鎖的流程
  1. readerCount - 1
  2. 以readerCount<0,判斷是否有寫鎖
  3. 沒有寫鎖的話,直接返回
  4. 有寫鎖的話,調用rUnlockSlow方法,readerWait - 1
  5. 若是readerWait == 0, 說明當前goroutine是寫鎖等待的最後一個讀鎖goroutine,須要喚醒寫鎖goroutine
獲取寫鎖的流程
  1. 先獲取互斥鎖
  2. readerCount - rwmutexMaxReaders,後續讀操做所有阻塞
  3. readerWait += readerCount,把當前正在執行讀操做的數量加到readerWait上
  4. 若是readerWait != 0 ,說明當前還有其餘goroutine持有讀鎖,當前goroutine進入睡眠,等待喚醒
釋放寫鎖流程
  1. readerCount + rwmutexMaxReaders, 後續讀鎖不會阻塞
  2. readerCount表明以前被寫鎖阻塞的讀鎖goroutine個數,喚醒readerCount個讀鎖goroutine
  3. 最後釋放互斥鎖
最後

RWMutex相對Mutex,增長了讀鎖的控制,就代碼邏輯複雜度而言,RWMutex比Mutex要簡單不少,對Mutex的流程熟悉的話,很快就能掌握RWMutex的原理ui

相關文章
相關標籤/搜索