最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文掌握go的sync.RWMutex鎖

 更新時間:2023年03月09日 15:06:21   作者:kina100  
這篇文章主要介紹了一文掌握go的sync.RWMutex鎖,本文是為了在面試中能快速口述RW鎖,并非為了完整解答RW鎖的機(jī)制,需要的朋友可以參考下

在簡略的說之前,首先要對RW鎖的結(jié)構(gòu)有一個大致的了解

type RWMutex struct {
    w           Mutex  // 寫鎖互斥鎖,只鎖寫鎖,和讀鎖無關(guān)
    writerSem   uint32 // sema鎖--用于“寫協(xié)程”排隊等待
    readerSem   uint32 // sema鎖--用于“讀協(xié)程”排隊等待
    readerCount int32  // 讀鎖的計數(shù)器
    readerWait  int32  // 等待讀鎖釋放的數(shù)量
}

這里要額外說一句,writerSem和readerSem底層都是semaRoot,這個結(jié)構(gòu)體有興趣可以了解下,他的用法有點類似于一個簡版的channel,很多地方把他的初始值設(shè)置為0,使得所有想獲取該sema鎖的協(xié)程都排隊等待,也就是說初始值為0的sema鎖,他本身起到的作用是成為一個協(xié)程等待隊列,就像沒有緩沖區(qū)的channel一樣。

好現(xiàn)在進(jìn)入正題。本文是為了在面試中能快速口述RW鎖,并非為了完整解答RW鎖的機(jī)制。

前提:

readerCount這個參數(shù)非常重要

  • 為負(fù)數(shù)時:說明此鎖已經(jīng)被寫協(xié)程占據(jù),所有渴望加讀鎖的協(xié)程被阻塞在readerSem
  • 為正數(shù)時:正數(shù)的數(shù)值為當(dāng)前持有該鎖的所有讀協(xié)程的數(shù)量總和,所有渴望加寫鎖的協(xié)程被阻塞在writerSem

讀寫鎖互斥性

  • 讀鎖是并發(fā)的,可以多個協(xié)程持有一把讀鎖。
  • 寫鎖是唯一的,互斥的,同一時刻只能有一個寫協(xié)程擁有寫鎖
  • 讀鎖和寫鎖是互斥的,寫鎖生效時,是不能有讀鎖被獲取到,同樣,必須所有的讀鎖都被釋放,或者壓根沒有讀協(xié)程獲取讀鎖,寫鎖方可被獲取。

一個很重要的參數(shù):const rwmutexMaxReaders = 1 << 30 ,rwmutexMaxReaders 非常大,意思是最多能有rwmutexMaxReaders(1 << 30  十進(jìn)制為  4294967296)個協(xié)程同時持有讀鎖。

寫鎖上鎖場景:

首先分析寫鎖,因為讀鎖的很多操作是根據(jù)寫鎖來的,如果一上來就說讀鎖,很多東西沒法串起來

 func (rw *RWMutex) Lock() {
    // race.Enabled是官方的一些測試,性能檢測的東西,無需關(guān)心,這個只在編譯階段才能啟用
	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.
	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
	// Wait for active readers.
	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
		runtime_SemacquireMutex(&rw.writerSem, false, 0)
	}
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
		race.Acquire(unsafe.Pointer(&rw.writerSem))
	}
}      

1.獲取寫鎖--沒有讀鎖等待

  • rw.w.Lock進(jìn)行加鎖,阻塞后續(xù)的其他寫協(xié)程的鎖請求。
  • atomic.AddInt32進(jìn)行原子操作,減去rwmutexMaxReaders,減成功才說明沒有并發(fā)問題,可以繼續(xù)下面的操作。然后再加上rwmutexMaxReaders,得到真正的readerCount的數(shù)值。
  • 此時還需要再次進(jìn)行一個原子操作,把當(dāng)前readerCount的值搬運到readerWait里面,意思是當(dāng)前要獲取寫鎖的協(xié)程需要等待的讀鎖的數(shù)量。
  • 此時readerCount只有兩種情況,一種是0,一種是正數(shù),因為只有寫鎖上的時候才為負(fù)數(shù),而上面的操作已經(jīng)還原了加寫鎖之前的值,而w.Lock保證了不會有2個及以上的寫協(xié)程去同時操作
  • readerCount 如果是 0,加鎖成功。
  • 如果不為0則說明有讀鎖等待,詳見場景2

2.獲取寫鎖--有讀鎖等待

  • 接上面的判斷,如果readrCount不為0,說明前面有讀鎖正在運行,寫鎖需要等待所有讀鎖釋放才能獲取寫鎖,當(dāng)前協(xié)程執(zhí)行 runtime_SemacquireMutex 進(jìn)入 waiterSem 的休眠隊列等待被喚醒

3.獲取寫鎖--前面已經(jīng)有寫鎖了

后面的寫協(xié)程也調(diào)用 rw.w.Lock() 進(jìn)行加鎖,因為前面有寫鎖已經(jīng)獲取了w,所以后續(xù)的寫協(xié)程會因為獲取不到w,而進(jìn)入到w的sema隊列里面,w是一個mutex的鎖,mutex鎖里是一個sema鎖,sema鎖因為沒有設(shè)置初始值,所以退化為一個隊列,而獲取不到w鎖的就會直接被阻塞在w的sema隊列里,從而無法進(jìn)行接下來的操作

寫鎖釋放鎖場景:

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")
	}
	// Unblock blocked readers, if any.
	for i := 0; i < int(r); i++ {
		runtime_Semrelease(&rw.readerSem, false, 0)
	}
	// Allow other writers to proceed.
	rw.w.Unlock()
	if race.Enabled {
		race.Enable()
	}
}

1.釋放寫鎖--后面【沒有】讀鎖等待

  • 執(zhí)行atomic.AddInt32進(jìn)行原子操作,把已經(jīng)為負(fù)值的readerCount還原為正數(shù),此時已經(jīng)算釋放了寫鎖
  • (此步驟不重要,就是個判錯)如果還原后的readerCount比rwmutexMaxReaders還大,這就是說明出錯了,直接throw彈出錯誤,throw這個方法是內(nèi)部方法,對go來說就是panic了
  • 此場景因為沒有讀鎖等待,此時的readerCount為0,不會進(jìn)入for循環(huán),直接rw.w.Unlock釋放w鎖,允許其他寫協(xié)程加鎖,此時其他的寫協(xié)程會被從w里的sema隊列里喚醒

2.釋放寫鎖--后面【有】讀鎖等待

  • 接場景1,原子操作readerCount釋放寫鎖后,如果r是大于0,說明有讀鎖等待,for循環(huán)readerSem里面所有的等待的讀協(xié)程,因為讀鎖是共享鎖,所以所有的讀協(xié)程都會獲取鎖并被喚醒
  • rw.w.Unlock釋放w鎖,允許其他寫協(xié)程加鎖,其他的寫協(xié)程會被從w里的sema隊列里喚醒

3.釋放寫鎖--后面有【寫鎖】等待

  • 上接場景2,當(dāng)rw.w.Unlock釋放w鎖,其他的寫協(xié)程會被從w里的sema隊列里喚醒
  • 寫鎖釋放的時候,是先喚醒所有等待的讀鎖,再解除rw.w鎖,所以,并不會造成讀鎖的饑餓
  • 后面讀鎖再次對rw.w進(jìn)行上鎖,重復(fù)上面所述寫鎖獲取鎖的場景

讀鎖上鎖場景:

func (rw *RWMutex) RLock() {
    // race.Enabled都是測試用的代碼,在閱讀源碼的時候可以跳過
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
    
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
		// A writer is pending, wait for it.
		runtime_SemacquireMutex(&rw.readerSem, false, 0)
	}
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
	}
}

1.獲取讀鎖--此時沒有寫鎖.

最簡單的場景,協(xié)程對rw.readerCount進(jìn)行原子操作加一,如果得到的結(jié)果為正數(shù),說明獲取讀鎖成功。

2.獲取讀鎖--前方已經(jīng)有寫鎖搶占了該鎖

  • 當(dāng)協(xié)程對rw.readerCount進(jìn)行原子加1操作的時候,發(fā)現(xiàn)加完,readerCount還是負(fù)數(shù),說明在這個時間點以前,已經(jīng)有協(xié)程獲取了寫鎖
  •  runtime_SemacquireMutex 方法將當(dāng)前協(xié)程加入readerSem隊列,等待寫鎖釋放后被批量喚醒(寫鎖釋放會一次性放出所有的堆積的讀協(xié)程)

3.獲取讀鎖--前方有寫鎖搶已經(jīng)被搶占,后方有寫鎖等待

  • 寫鎖在獲取的時候,對RWMutex.w進(jìn)行加鎖,是獨占鎖,如果前方一個寫鎖已經(jīng)得到了鎖正在處理業(yè)務(wù),那么后方的寫鎖進(jìn)來就會發(fā)現(xiàn)加不上鎖,直接在rw.w.lock階段就阻塞了,后面的邏輯是無法繼續(xù)運行的,所以進(jìn)入不了writerSem,它只會進(jìn)入到w這個mutex鎖的sema隊列里,讀鎖則進(jìn)入休眠隊列readerSem

讀鎖釋放鎖場景:

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 {
		// Outlined slow-path to allow the fast-path to be inlined
		rw.rUnlockSlow(r)
	}
	if race.Enabled {
		race.Enable()
	}
}

1.釋放讀鎖--后方?jīng)]有寫鎖等待

  • atomic.AddInt32 進(jìn)行原子操作,讓readerCount 減1,操作后,如果readerCount 大于0,說明后方是沒有寫鎖等待的,釋放鎖后整個流程就結(jié)束了

2.釋放讀鎖--后方有寫鎖等待

  • 原子操作eaderCount 減1后,發(fā)現(xiàn)eaderCount是小于0的,此時說明已經(jīng)有等待寫鎖的協(xié)程在嘗試獲取寫鎖。執(zhí)行 rw.rUnlockSlow(r) 。               
func (rw *RWMutex) rUnlockSlow(r int32) {
	if r+1 == 0 || r+1 == -rwmutexMaxReaders {
		race.Enable()
		throw("sync: RUnlock of unlocked RWMutex")
	}
	// A writer is pending.
	if atomic.AddInt32(&rw.readerWait, -1) == 0 {
		// The last reader unblocks the writer.
		runtime_Semrelease(&rw.writerSem, false, 1)
	}
}

這里是有個前提的,上面提到(詳見上面的獲取寫鎖的場景1),如果寫協(xié)程進(jìn)來想加寫鎖,需要把它需要等待的讀鎖數(shù)量從readerCount里賦值給readerWait。當(dāng)它等待的讀鎖釋放后,就需要用rUnlockSlow方法對readerWait進(jìn)行減1,如果readWait == 0 ,說明這是最后一個需要等待的讀鎖也釋放了,釋放后就通知該寫鎖可以被喚醒了,鎖給你了。

到此這篇關(guān)于go的sync.RWMutex鎖的文章就介紹到這了,更多相關(guān)go的sync.RWMutex鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • 解決Golang小數(shù)float64在實際工程中加減乘除的精度問題

    解決Golang小數(shù)float64在實際工程中加減乘除的精度問題

    這篇文章主要介紹了解決Golang小數(shù)float64在實際工程中加減乘除的精度問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • Go語言基礎(chǔ)知識點介紹

    Go語言基礎(chǔ)知識點介紹

    在本篇文章里小編給大家整理的是一篇關(guān)于Go語言基礎(chǔ)知識點介紹內(nèi)容,有興趣的朋友們可以跟著學(xué)習(xí)參考下。
    2021-07-07
  • Go內(nèi)置序列化庫gob的使用

    Go內(nèi)置序列化庫gob的使用

    本文主要介紹了Go內(nèi)置序列化庫gob的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Go語言中的上下文取消操作詳解

    Go語言中的上下文取消操作詳解

    這篇文章主要給大家介紹了關(guān)于Go語言中上下文取消操作的相關(guān)資料,本文將解釋我們?nèi)绾卫蒙舷挛膸斓娜∠匦?,并通過一些模式和最佳實踐來使用取消,使你的程序更快、更健壯。需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • 重學(xué)Go語言之變量與常量的聲明與使用詳解

    重學(xué)Go語言之變量與常量的聲明與使用詳解

    變量、常量的聲明與使用是掌握一門編程語言的基礎(chǔ),在這篇文章中,小編就來帶大家學(xué)習(xí)一下Go語言是怎么樣聲明和使用變量與常量吧
    2023-03-03
  • Golang?Gin解析JSON請求數(shù)據(jù)避免出現(xiàn)EOF錯誤

    Golang?Gin解析JSON請求數(shù)據(jù)避免出現(xiàn)EOF錯誤

    這篇文章主要為大家介紹了Golang?Gin?優(yōu)雅地解析JSON請求數(shù)據(jù),避免ShouldBindBodyWith出現(xiàn)EOF錯誤的源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • 手把手帶你走進(jìn)Go語言之常量解析

    手把手帶你走進(jìn)Go語言之常量解析

    這篇文章主要介紹了Go語言之常量解析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • Go語言中的IO操作及Flag包的用法

    Go語言中的IO操作及Flag包的用法

    這篇文章介紹了Go語言中的IO操作及Flag包的用法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • go代碼實現(xiàn)買房貸款月供計算的方法

    go代碼實現(xiàn)買房貸款月供計算的方法

    今天小編就為大家分享一篇關(guān)于go代碼實現(xiàn)買房貸款月供計算的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • 詳解Go語言中如何高效遍歷目錄

    詳解Go語言中如何高效遍歷目錄

    目錄遍歷是一個很常見的操作,它的使用場景有如文件目錄查看、文件系統(tǒng)清理、日志分析、項目構(gòu)建等,本文將詳細(xì)介紹在Go中幾種遍歷目錄文件的方法,需要的可以參考下
    2024-02-02

最新評論

大姚县| 芮城县| 台北县| 于都县| 新野县| 原阳县| 淅川县| 兖州市| 游戏| 壤塘县| 安丘市| 湖口县| 黎川县| 武冈市| 剑河县| 社会| 博罗县| 津南区| 兴化市| 晋中市| 灌南县| 宿松县| 微博| 漳浦县| 乐亭县| 和平县| 贡觉县| 莎车县| 土默特左旗| 乌兰察布市| 微山县| 淅川县| 红安县| 扶余县| 广东省| 蒲城县| 栾城县| 高淳县| 吉木萨尔县| 招远市| 页游|