golang RWMutex讀寫互斥鎖原始碼分析

夢朝思夕發表於2018-06-11

針對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)
}
複製程式碼

總結

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

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

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

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

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

相關文章