golang RWMutex讀寫鎖分析


RWMutex:是基於Mutex實現的讀寫互斥鎖,一個goroutine可以持有多個讀鎖或者一個寫鎖,同一時刻只能持有讀鎖或者寫鎖

數據結構設計:

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))
    }
}

按順序這里應該介紹釋放寫鎖的代碼了,但是由於獲取寫鎖中有很重要的幾個邏輯變量,跟獲取讀鎖時強依賴,所以在這里先說說獲取讀鎖的邏輯

// 獲取讀鎖
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()
    }
}

總結:

讀寫互斥鎖的實現比較有技巧性一些,需要幾點

1. 讀鎖不能阻塞讀鎖,引入readerCount實現

2. 讀鎖需要阻塞寫鎖,直到所以讀鎖都釋放,引入readerSem實現

3. 寫鎖需要阻塞讀鎖,直到所以寫鎖都釋放,引入wirterSem實現

4. 寫鎖需要阻塞寫鎖,引入Metux實現

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM