sync包 rwmutex源碼閱讀

借鑒于Go夜讀鲜屏,加了個人理解:https://reading.developerlearning.cn/articles/sync/sync_rwmutex_source_code_analysis/

go版本:go1.12 windows/amd64

sync/rwmutex.go是對 runtime/rwmutex.go文件的拷貝业汰。

當(dāng)一個協(xié)程獲取到讀鎖,同時存在另一個協(xié)程調(diào)用寫鎖嫉沽,這時不允許新的協(xié)程來獲取讀鎖直到最新的讀鎖被釋放昔逗;這能確保在遞歸循環(huán)讀鎖的情況下,寫鎖也能被調(diào)用至非,寫鎖阻止新的reader來獲取lock.

也就是說钠署,上寫鎖時要等之前的讀鎖釋放,新的讀操作會卡住荒椭,等待舊的讀操作完谐鼎,寫鎖運行完,卡住的讀操作才會繼續(xù)進(jìn)行趣惠。

結(jié)構(gòu)體

// If a goroutine holds a RWMutex for reading and another goroutine might
// call Lock, no goroutine should expect to be able to acquire a read lock
// until the initial read lock is released. In particular, this prohibits
// recursive read locking. This is to ensure that the lock eventually becomes
// available; a blocked Lock call excludes new readers from acquiring the
// lock.
type RWMutex struct {
    w           Mutex  // 互斥鎖
    writerSem   uint32 // 寫鎖信號量
    readerSem   uint32 // 讀鎖信號量
    readerCount int32  // 還未釋放讀鎖的reader數(shù)
    readerWait  int32  // 獲取寫鎖時需要等待的讀鎖釋放數(shù)量
}

常量

const rwmutexMaxReaders = 1 << 30   // 支持最多2^30個讀鎖

方法

以下是 sync.RWMutex 提供的4個方法

RLock

// RLock locks rw for reading.
//
// It should not be used for recursive read locking; a blocked Lock
// call excludes new readers from acquiring the lock
func (rw *RWMutex) RLock() { //讀鎖
   if race.Enabled {
       _ = rw.w.state
       race.Disable()
   }
   // 每次 goroutine獲取讀鎖時狸棍, readCount+1
   // 如果寫鎖已經(jīng)被獲取身害,那么 readCount 在 -rwmutexMaxReaders 與 0 之間(當(dāng)為0的時候,代表有2^30個讀鎖在等待隔缀,應(yīng)該會出錯题造,但是極端條件不會出現(xiàn))
   // 通過readCount 判斷讀鎖與寫鎖 是否互斥傍菇,如果有寫鎖存在就掛起 goroutine猾瘸,多個讀鎖可以并行
   if atomic.AddInt32(&rw.readerCount, 1) < 0 { 
       // A writer is pending, wait for it.
       runtime_Semacquire(&rw.readerSem) //等待reader信號量
   }
   if race.Enabled {
       race.Enable()
       race.Acquire(unsafe.Pointer(&rw.readerSem))
   }
}

RUnlock

// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
func (rw *RWMutex) RUnlock() {  //釋放讀鎖
   if race.Enabled {
       _ = rw.w.state
       race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
       race.Disable()
   }
  //  檢查當(dāng)前是否可以進(jìn)行釋放鎖
   if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 { 
       // 1.r+1==0時构挤,rw.readerCount -1= -1,rw.readerCount  = 0則不存在讀鎖,表示直接執(zhí)行RUnlock()
       // 2.r+1=-rwmutexMaxReaders,rw.readerCount = -rwmutexMaxReaders ,
       // 這種情況出現(xiàn)在獲取Lock()方法嫁怀,atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders),這時rw.readerCount  = 0 也不存在讀鎖,表示執(zhí)行Lock()再執(zhí)行RUnlock()
       if r+1 == 0 || r+1 == -rwmutexMaxReaders { //如果已經(jīng)沒有讀鎖的陪踩,還去釋放(如釋放多次)
           race.Enable()
           throw("sync: RUnlock of unlocked RWMutex")
       }
       // A writer is pending. 下面的情況代表有寫鎖
       if atomic.AddInt32(&rw.readerWait, -1) == 0 { //寫鎖的reader wait數(shù)量-1
           // The last reader unblocks the writer. 
           runtime_Semrelease(&rw.writerSem, false)//如果wait數(shù)量到0咐低,釋放writer信號量 
       }
   }
   if race.Enabled {
       race.Enable()
   }
}

Lock

// Lock locks rw for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
func (rw *RWMutex) Lock() { // 寫鎖加鎖操作
    if race.Enabled {
        _ = rw.w.state
        race.Disable()
    }
    // First, resolve competition with other writers.
    rw.w.Lock() //上互斥鎖
    // Announce to readers there is a pending writer.
    // 將當(dāng)前的 readerCount 置為負(fù)數(shù)揽思,告訴 RUnLock 當(dāng)前存在寫鎖等待
    r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) +  rwmutexMaxReaders  
    // Wait for active readers. 
   // 等待讀鎖釋放
    if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {//當(dāng)前readCount不為0,并且上一步r計算之后RUnlock的次數(shù)和之前readerCount相同(上一步計算后可能有多次RUnlock,readerWait會變成負(fù)數(shù))
        runtime_Semacquire(&rw.writerSem)//等待writer信號量
    }
    if race.Enabled {
        race.Enable()
        race.Acquire(unsafe.Pointer(&rw.readerSem))
        race.Acquire(unsafe.Pointer(&rw.writerSem))
    }
}

Unlock

// Unlock unlocks rw for writing. It is a run-time error if rw is
// not locked for writing on entry to Unlock. 
//
// As with Mutexes, a locked RWMutex is not associated with a particular
// goroutine. One goroutine may RLock (Lock) a RWMutex and then
// arrange for another goroutine to RUnlock (Unlock) it.
func (rw *RWMutex) Unlock() {
    if race.Enabled {
        _ = rw.w.state
        race.Release(unsafe.Pointer(&rw.readerSem))
        race.Release(unsafe.Pointer(&rw.writerSem))
        race.Disable()
    }

    // 加上 Lock 的時候減去的 rwmutexMaxReaders
    r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
    // 1.沒執(zhí)行Lock調(diào)用Unlock 2.釋放寫鎖多次 拋出異常
    if r >= rwmutexMaxReaders {
        race.Enable()
        throw("sync: Unlock of unlocked RWMutex")
    }
    // 通知當(dāng)前等待的讀鎖
    for i := 0; i < int(r); i++ {
        runtime_Semrelease(&rw.readerSem, false)
    }
    // 釋放 Mutex 鎖
    rw.w.Unlock()
    if race.Enabled {
        race.Enable()
    }
}

RLocker
只對讀操作加解鎖

// RLocker returns a Locker interface that implements
// the Lock and Unlock methods by calling rw.RLock and rw.RUnlock.
func (rw *RWMutex) RLocker() Locker {
    return (*rlocker)(rw)
}

type rlocker RWMutex

func (r *rlocker) Lock()   { (*RWMutex)(r).RLock() }
func (r *rlocker) Unlock() { (*RWMutex)(r).RUnlock() }

思考

當(dāng)調(diào)用寫鎖時见擦,新的讀鎖會掛起钉汗,等待已經(jīng)執(zhí)行的讀鎖執(zhí)行完,然后才執(zhí)行寫鎖,鎖的顆粒度應(yīng)盡量小鲤屡。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末损痰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子酒来,更是在濱河造成了極大的恐慌卢未,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堰汉,死亡現(xiàn)場離奇詭異辽社,居然都是意外死亡,警方通過查閱死者的電腦和手機翘鸭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門滴铅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人就乓,你說我怎么就攤上這事汉匙。” “怎么了档址?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵盹兢,是天一觀的道長。 經(jīng)常有香客問我守伸,道長绎秒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任尼摹,我火速辦了婚禮见芹,結(jié)果婚禮上剂娄,老公的妹妹穿的比我還像新娘。我一直安慰自己玄呛,他們只是感情好阅懦,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著徘铝,像睡著了一般耳胎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惕它,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天怕午,我揣著相機與錄音,去河邊找鬼淹魄。 笑死郁惜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的甲锡。 我是一名探鬼主播兆蕉,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼缤沦!你這毒婦竟也來了虎韵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤疚俱,失蹤者是張志新(化名)和其女友劉穎劝术,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呆奕,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡养晋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了梁钾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绳泉。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖姆泻,靈堂內(nèi)的尸體忽然破棺而出零酪,到底是詐尸還是另有隱情,我是刑警寧澤拇勃,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布四苇,位于F島的核電站,受9級特大地震影響方咆,放射性物質(zhì)發(fā)生泄漏月腋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望榆骚。 院中可真熱鬧片拍,春花似錦、人聲如沸妓肢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碉钠。三九已至纲缓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間放钦,已是汗流浹背色徘。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留操禀,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓横腿,卻偏偏與公主長得像颓屑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子耿焊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容