golang sync.RWMutex

什麼是sync.RWMutex

首先看一下RWMutex的定義golang

RWMutex是讀/寫互斥鎖。鎖能夠由任意數量的讀者或單個寫者持有。 RWMutex的零值是未鎖定的互斥鎖bash

這個package包含了4個方法函數

func (rw *RWMutex) Lock // Lock locks rw for writing. func (rw *RWMutex) RLock // RLock locks rw for reading. func (rw *RWMutex) RUnlock // Unlock locks rw for reading func (rw *RWMutex) Unlock // Unlock unlocks rw for writing 複製代碼

本次來介紹一下sync.RWMutex的實現,仍是先舉例ui

var mu sync.RWMutex
var data map[string]string

func main() {
	data = map[string]string{"hoge": "fuga"}
	mu = sync.RWMutex{}
	go read()
	go read()
	go write()
	go read()
	time.Sleep(5 * time.Second)
}
// 讀方法
func read() {
	println("read_start")
	mu.RLock()
	defer mu.RUnlock()
	time.Sleep(1*time.Second)
	println("read_complete", data["hoge"])
}
// 寫方法
func write() {
	println("write_start")
	//mu.Lock() 仔細看下這兩行代碼,此處是註釋掉的
	//defer mu.Unlock()
	time.Sleep(2 * time.Second)
	data["hoge"] = "piyo"
	println("write_complete")
}
複製代碼

註釋掉上述兩行代碼會輸出什麼呢?atom

read_start
read_start
write_start
read_start
read_complete fuga
read_complete fuga
read_complete fuga
write_complete
複製代碼

若是把上述代碼註釋掉的地方打開結果會有影響嗎?答案是確定的!spa

read_start
write_start
read_start
read_start
read_complete fuga
write_complete
// 能夠看到 read_complete已經被write的字串替換掉了
read_complete piyo
read_complete piyo
複製代碼

此時就須要講解下sync.RWMutex鎖的實現了,先看下RWMutex的定義code

type RWMutex struct {
	w           Mutex  // held if there are pending writers
	writerSem   uint32 // semaphore(信號) for writers to wait for completing readers
	readerSem   uint32 // semaphore for readers to wait for completing writers
	readerCount int32  // number of pending readers
	readerWait  int32  // number of departing(離開) readers
}
複製代碼

RLock & RUnlock 讀鎖 & 釋放讀鎖

設定最大的讀鎖數量 
const rwmutexMaxReaders = 1 << 30

RLock鎖定rw進行閱讀。
它不該該用於遞歸讀鎖定;被阻止的鎖調用排除新讀者獲取鎖定。

func (rw *RWMutex) RLock() {

	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
	    // 一個寫者正在等待,waiting...
		runtime_SemacquireMutex(&rw.readerSem, false)
	}
	// 靜態檢測
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
	}
}
複製代碼

此處atomic.AddInt32(&rw.readerCount,1)執行的是原子加操做,此時有兩種場景:遞歸

  • 此時無寫鎖 (readerCount + 1) > 0 RLock正常結束
  • 此時有寫鎖 (readerCount + 1) < 0 等待寫鎖結束
RUnlock撤銷單個RLock調用;它不會影響其餘同時閱讀的讀者。
若是在進入RUnlock時沒有鎖定rw進行讀取,則會出現運行時錯誤。
func (rw *RWMutex) RUnlock() {

	if race.Enabled {
		_ = rw.w.state
		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
		race.Disable()
	}
	
	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
		if r+1 == 0 || r+1 == -rwmutexMaxReaders {
			race.Enable()
			// 報錯,解鎖了一個未鎖的RWMutex
			throw("sync: RUnlock of unlocked RWMutex")
		}
		// 若是此時沒有讀者等待
		if atomic.AddInt32(&rw.readerWait, -1) == 0 {
			// The last reader unblocks the writer.
			runtime_Semrelease(&rw.writerSem, false)
		}
	}
	if race.Enabled {
		race.Enable()
	}
}
複製代碼
  • runtime_SemacquireMutex函數是goroutine等待隊列的enqueue
  • runtime_Semrelease函數是goroutine等待隊列的dequeue操做

atomic.AddInt32(&rw.readerCount, -1)此處也有幾種場景 (ps:此處只考慮單個讀寫鎖)隊列

讀鎖&寫鎖 寫鎖 讀鎖
readerCount -(1<<30) panic 0
readerWait 0 panic 0

Lock & UnLock 寫鎖&釋放寫鎖

Lock鎖定用於寫入。若是鎖已經鎖定以進行讀取或寫入,則Lock將鎖定,直到鎖定可用。
func (rw *RWMutex) Lock() {
       // 靜態檢測
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	
	// 首先,與其餘寫者解決競爭問題(這裏採用了互斥鎖)
	rw.w.Lock()
	
	// 若是一個寫者獲得了鎖,readerCount = - (1<<30) , r = 0
	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
	
	// 等待活躍的讀者
	// r!=0說明有讀者加鎖,此處需等待
	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
		runtime_SemacquireMutex(&rw.writerSem, false)
	}
	
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
		race.Acquire(unsafe.Pointer(&rw.writerSem))
	}
}
func (rw *RWMutex) Unlock() {
    // 靜態檢測
	if race.Enabled {
		_ = rw.w.state
		race.Release(unsafe.Pointer(&rw.readerSem))
		race.Disable()
	}

	// Announce to readers there is no active writer.
	r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
	if r >= rwmutexMaxReaders {
		race.Enable()
		throw("sync: Unlock of unlocked RWMutex")
	}
	// 循環等待讀者完成
	for i := 0; i < int(r); i++ {
		runtime_Semrelease(&rw.readerSem, false)
	}
	// Allow other writers to proceed.(繼續進行)
	rw.w.Unlock()
	if race.Enabled {
		race.Enable()
	}
}
複製代碼
讀鎖&寫鎖 寫鎖 讀鎖
readerCount 1 0 panic
readerWait 1 0 panic

that's allstring

相關文章
相關標籤/搜索