成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

如何理解golang里面的讀寫鎖實現(xiàn)與核心原理

如何理解golang里面的讀寫鎖實現(xiàn)與核心原理,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

創(chuàng)新互聯(lián)建站服務項目包括慈利網(wǎng)站建設、慈利網(wǎng)站制作、慈利網(wǎng)頁制作以及慈利網(wǎng)絡營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關系等,向廣大中小型企業(yè)、政府機構等提供互聯(lián)網(wǎng)行業(yè)的解決方案,慈利網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務的客戶以成都為中心已經(jīng)輻射到慈利省份的部分城市,未來相信會繼續(xù)擴大服務區(qū)域并繼續(xù)獲得客戶的支持與信任!

基礎筑基

讀寫鎖的特點

讀寫鎖區(qū)別與互斥鎖的主要區(qū)別就是讀鎖之間是共享的,多個goroutine可以同時加讀鎖,但是寫鎖與寫鎖、寫鎖與讀鎖之間則是互斥的

寫鎖饑餓問題

因為讀鎖是共享的,所以如果當前已經(jīng)有讀鎖,那后續(xù)goroutine繼續(xù)加讀鎖正常情況下是可以加鎖成功,但是如果一直有讀鎖進行加鎖,那嘗試加寫鎖的goroutine則可能會長期獲取不到鎖,這就是因為讀鎖而導致的寫鎖饑餓問題

基于高低位與等待隊列的實現(xiàn)

如何理解golang里面的讀寫鎖實現(xiàn)與核心原理 在說golang之前介紹一種JAVA里面的實現(xiàn),在JAVA中ReentrantReadWriteLock實現(xiàn)采用一個state的高低位來進行讀寫鎖的計數(shù),其中高16位存儲讀的計數(shù),低16位存儲寫的計數(shù),并配合一個AQS來實現(xiàn)排隊等待機制,同時AQS中的每個waiter都會有一個status,用來標識自己的狀態(tài)

golang的讀寫鎖的實現(xiàn)

成員變量

如何理解golang里面的讀寫鎖實現(xiàn)與核心原理

結構體

type RWMutex struct {
	w           Mutex  // held if there are pending writers
	writerSem   uint32 // 用于writer等待讀完成排隊的信號量
	readerSem   uint32 // 用于reader等待寫完成排隊的信號量
	readerCount int32  // 讀鎖的計數(shù)器
	readerWait  int32  // 等待讀鎖釋放的數(shù)量
}

寫鎖計數(shù)

讀寫鎖中允許加讀鎖的最大數(shù)量是4294967296,在go里面對寫鎖的計數(shù)采用了負值進行,通過遞減最大允許加讀鎖的數(shù)量從而進行寫鎖對讀鎖的搶占

const rwmutexMaxReaders = 1 << 30

讀鎖實現(xiàn)

讀鎖加鎖邏輯

如何理解golang里面的讀寫鎖實現(xiàn)與核心原理

func (rw *RWMutex) RLock() {
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	// 累加reader計數(shù)器,如果小于0則表明有writer正在等待
	if atomic.AddInt32(&rw.readerCount, 1) < 0 {
		// 當前有writer正在等待讀鎖,讀鎖就加入排隊
		runtime_SemacquireMutex(&rw.readerSem, false)
	}
	if race.Enabled {
		race.Enable()
		race.Acquire(unsafe.Pointer(&rw.readerSem))
	}
}

讀鎖釋放邏輯

如何理解golang里面的讀寫鎖實現(xiàn)與核心原理

func (rw *RWMutex) RUnlock() {
	if race.Enabled {
		_ = rw.w.state
		race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
		race.Disable()
	}
	// 如果小于0,則表明當前有writer正在等待
	if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
		if r+1 == 0 || r+1 == -rwmutexMaxReaders {
			race.Enable()
			throw("sync: RUnlock of unlocked RWMutex")
		}
		// 將等待reader的計數(shù)減1,證明當前是已經(jīng)有一個讀的,如果值==0,則進行喚醒等待的
		if atomic.AddInt32(&rw.readerWait, -1) == 0 {
			// The last reader unblocks the writer.
			runtime_Semrelease(&rw.writerSem, false)
		}
	}
	if race.Enabled {
		race.Enable()
	}
}

寫鎖實現(xiàn)

加寫鎖實現(xiàn)

如何理解golang里面的讀寫鎖實現(xiàn)與核心原理

func (rw *RWMutex) Lock() {
	if race.Enabled {
		_ = rw.w.state
		race.Disable()
	}
	// 首先獲取mutex鎖,同時多個goroutine只有一個可以進入到下面的邏輯
	rw.w.Lock()
	// 對readerCounter進行進行搶占,通過遞減rwmutexMaxReaders允許最大讀的數(shù)量
    // 來實現(xiàn)寫鎖對讀鎖的搶占
	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
	// 記錄需要等待多少個reader完成,如果發(fā)現(xiàn)不為0,則表明當前有reader正在讀取,當前goroutine
    // 需要進行排隊等待
	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))
	}
}

釋放寫鎖

如何理解golang里面的讀寫鎖實現(xiàn)與核心原理

func (rw *RWMutex) Unlock() {
	if race.Enabled {
		_ = rw.w.state
		race.Release(unsafe.Pointer(&rw.readerSem))
		race.Disable()
	}

	// 將reader計數(shù)器復位,上面減去了一個rwmutexMaxReaders現(xiàn)在再重新加回去即可復位
	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)
	}
	// 釋放mutex
	rw.w.Unlock()
	if race.Enabled {
		race.Enable()
	}
}

關鍵核心機制

寫鎖對讀鎖的搶占

加寫鎖的搶占

	// 在加寫鎖的時候通過將readerCount遞減最大允許加讀鎖的數(shù)量,來實現(xiàn)對加讀鎖的搶占
	r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders

加讀鎖的搶占檢測

// 如果沒有寫鎖的情況下讀鎖的readerCount進行Add后一定是一個>0的數(shù)字,這里通過檢測值為負數(shù)
//就實現(xiàn)了讀鎖對寫鎖搶占的檢測
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
		// A writer is pending, wait for it.
		runtime_SemacquireMutex(&rw.readerSem, false)
	}

寫鎖搶占讀鎖后后續(xù)的讀鎖就會加鎖失敗,但是如果想加寫鎖成功還要繼續(xù)對已經(jīng)加讀鎖成功的進行等待

	if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
        // 寫鎖發(fā)現(xiàn)需要等待的讀鎖釋放的數(shù)量不為0,就自己自己去休眠了
		runtime_SemacquireMutex(&rw.writerSem, false)
	}

寫鎖既然休眠了,則必定要有一種喚醒機制其實就是每次釋放鎖的時候,當檢查到有加寫鎖的情況下,就遞減readerWait,并由最后一個釋放reader lock的goroutine來實現(xiàn)喚醒寫鎖

		if atomic.AddInt32(&rw.readerWait, -1) == 0 {
			// The last reader unblocks the writer.
			runtime_Semrelease(&rw.writerSem, false)
		}

寫鎖的公平性

在加寫鎖的時候必須先進行mutex的加鎖,而mutex本身在普通模式下是非公平的,只有在饑餓模式下才是公平的

	rw.w.Lock()

寫鎖與讀鎖的公平性

在加讀鎖和寫鎖的工程中都使用atomic.AddInt32來進行遞增,而該指令在底層是會通過LOCK來進行CPU總線加鎖的,因此多個CPU同時執(zhí)行readerCount其實只會有一個成功,從這上面看其實是寫鎖與讀鎖之間是相對公平的,誰先達到誰先被CPU調(diào)度執(zhí)行,進行LOCK鎖cache line成功,誰就加成功鎖

可見性與原子性問題

在并發(fā)場景中特別是JAVA中通常會提到并發(fā)里面的兩個問題:可見性與內(nèi)存屏障、原子性, 其中可見性通常是指在cpu多級緩存下如何保證緩存的一致性,即在一個CPU上修改了了某個數(shù)據(jù)在其他的CPU上不會繼續(xù)讀取舊的數(shù)據(jù),內(nèi)存屏障通常是為了CPU為了提高流水線性能,而對指令進行重排序而來,而原子性則是指的執(zhí)行某個操作的過程的不可分割

底層實現(xiàn)的CPU指令

go里面并沒有volatile這種關鍵字,那如何能保證上面的AddInt32這個操作可以滿足上面的兩個問題呢, 其實關鍵就在于底層的2條指令,通過LOCK指令配合CPU的MESI協(xié)議,實現(xiàn)可見性和內(nèi)存屏障,同時通過XADDL則用來保證原子性,從而解決上面提到的可見性與原子性問題

	// atomic/asm_amd64.s TEXT runtime∕internal∕atomic·Xadd(SB)
	LOCK
	XADDL	AX, 0(BX)

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。

本文標題:如何理解golang里面的讀寫鎖實現(xiàn)與核心原理
URL標題:http://jinyejixie.com/article12/podigc.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、虛擬主機、微信公眾號、用戶體驗、網(wǎng)站設計公司、網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應式網(wǎng)站建設
龙门县| 铜鼓县| 崇礼县| 安图县| 大足县| 英吉沙县| 北安市| 内乡县| 北安市| 曲周县| 东乡| 沅陵县| 白河县| 济南市| 教育| 南昌市| 城步| 玉环县| 凭祥市| 松溪县| 枣庄市| 咸宁市| 定南县| 菏泽市| 沁阳市| 桓仁| 安徽省| 舞阳县| 双峰县| 昆山市| 当涂县| 岱山县| 沧源| 宽城| 康定县| 榕江县| 桐柏县| 鄂温| 陆河县| 万荣县| 城步|