【Go】sync.RWMutex源码分析
RWMutex
讀寫鎖相較于互斥鎖有更低的粒度,它允許并發(fā)讀,因此在讀操作明顯多于寫操作的場景下能減少鎖競爭的次數(shù),提高程序效率。
type RWMutex struct {w Mutex // held if there are pending writerswriterSem uint32 // semaphore for writers to wait for completing readersreaderSem uint32 // semaphore for readers to wait for completing writersreaderCount int32 // number of pending readersreaderWait int32 // number of departing readers }RWMutex 結(jié)構(gòu)體中包含五個字段,分別表示:
- w: 復(fù)用互斥鎖
- writerSem 和 readerSem: 用于寫等待讀和讀等待寫的信號量
- readerCount:
- readerWait: 等待寫鎖釋放的讀者數(shù)量
讀鎖
RLock
當(dāng)有 goroutine 寫時,是不允許讀的,這時會把 readerCount 設(shè)置為負(fù),這時讀 goroutine 應(yīng)該被阻塞
func (rw *RWMutex) RLock() {if atomic.AddInt32(&rw.readerCount, 1) < 0 {// 阻塞讀runtime_SemacquireMutex(&rw.readerSem, false, 0)} }RUnlock
讀鎖解鎖時只需要將 readerCount - 1, 如果結(jié)果小于零,說明:
寫鎖
Lock
func (rw *RWMutex) Lock() {// 獲取互斥寫鎖rw.w.Lock()// 阻塞讀r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders// 如果有人在讀,需要等待讀鎖釋放if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {// 阻塞等待讀鎖釋放runtime_SemacquireMutex(&rw.writerSem, false, 0)} }Lock 會先通過互斥鎖阻塞寫操作,然后禁止讀鎖獲取,等待已經(jīng)持有讀鎖的 goroutine 釋放讀鎖,這樣可以避免連續(xù)的寫操作使讀陷入饑餓。
Unlock
func (rw *RWMutex) Unlock() {// 重新允許讀鎖獲取r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)if r >= rwmutexMaxReaders {race.Enable()throw("sync: Unlock of unlocked RWMutex")}// 觸發(fā)等待中的寫鎖的信號量for i := 0; i < int(r); i++ {runtime_Semrelease(&rw.readerSem, false, 0)}// 互斥鎖解鎖rw.w.Unlock() }總結(jié)
在極端情況下:
- 如果完全沒有寫,讀鎖加鎖只是將 readerCount 加一,解鎖只是將其減一,不存在鎖競爭。
- 如果只有寫,加鎖和解鎖都比互斥鎖多了一個對 readerCount 取反操作
在一般情況下,讀鎖在獲取鎖前會檢查 readerCount, 如果為負(fù),說明有人在寫,則進(jìn)入阻塞狀態(tài),等待 readerSem 的信號,寫鎖獲取鎖在得到互斥鎖后會先設(shè)置 readerCount 為負(fù),阻止新的讀者獲取讀鎖,然后需要等待所有已經(jīng)持有讀鎖的 goroutine 釋放讀鎖,這里使用的是 readerWait ,當(dāng) readerCount 為負(fù)時,如果讀鎖被釋放,該量就會減一,當(dāng) readerWait == 0 時,則說明所有在寫鎖獲取之前獲得的讀鎖都被釋放了,最后一個釋放的讀鎖會通過 writerSem 通知寫對象。
寫鎖釋放時,需要通過 readerSem 信號觸發(fā)所有阻塞中的寫對象。
總結(jié)
以上是生活随笔為你收集整理的【Go】sync.RWMutex源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 既然谭浩强的C语言教材不好,那应该选什么
- 下一篇: 运筹学—最大流模型