中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

Go語(yǔ)言互斥鎖Mutex和讀寫鎖RWMutex的用法

這篇文章主要介紹“Go語(yǔ)言互斥鎖Mutex和讀寫鎖RWMutex的用法”,在日常操作中,相信很多人在Go語(yǔ)言互斥鎖Mutex和讀寫鎖RWMutex的用法問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Go語(yǔ)言互斥鎖Mutex和讀寫鎖RWMutex的用法”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

成都創(chuàng)新互聯(lián)公司是一家朝氣蓬勃的網(wǎng)站建設(shè)公司。公司專注于為企業(yè)提供信息化建設(shè)解決方案。從事網(wǎng)站開發(fā),網(wǎng)站制作,網(wǎng)站設(shè)計(jì),網(wǎng)站模板,微信公眾號(hào)開發(fā),軟件開發(fā),微信平臺(tái)小程序開發(fā),10余年建站對(duì)成都履帶攪拌車等多個(gè)行業(yè),擁有多年的網(wǎng)站運(yùn)維經(jīng)驗(yàn)。

sync.Mutex

Go中使用sync.Mutex類型實(shí)現(xiàn)mutex(排他鎖、互斥鎖)。在源代碼的sync/mutex.go文件中,有如下定義:

// A Mutex is a mutual exclusion lock. // The zero value for a Mutex is an unlocked mutex. // // A Mutex must not be copied after first use. type Mutex struct {    state int32 sema uint32 }

這沒(méi)有任何非凡的地方。和mutex相關(guān)的所有事情都是通過(guò)sync.Mutex類型的兩個(gè)方法sync.Lock()和sync.Unlock()函數(shù)來(lái)完成的,前者用于獲取sync.Mutex鎖,后者用于釋放sync.Mutex鎖。sync.Mutex一旦被鎖住,其它的Lock()操作就無(wú)法再獲取它的鎖,只有通過(guò)Unlock()釋放鎖之后才能通過(guò)Lock()繼續(xù)獲取鎖。

也就是說(shuō),已有的鎖會(huì)導(dǎo)致其它申請(qǐng)Lock()操作的goroutine被阻塞,且只有在Unlock()的時(shí)候才會(huì)解除阻塞

另外需要注意,sync.Mutex不區(qū)分讀寫鎖,只有Lock()與Lock()之間才會(huì)導(dǎo)致阻塞的情況,如果在一個(gè)地方Lock(),在另一個(gè)地方不Lock()而是直接修改或訪問(wèn)共享數(shù)據(jù),這對(duì)于sync.Mutex類型來(lái)說(shuō)是允許的,因?yàn)閙utex不會(huì)和goroutine進(jìn)行關(guān)聯(lián)。如果想要區(qū)分讀、寫鎖,可以使用sync.RWMutex類型,見后文。

在Lock()和Unlock()之間的代碼段稱為資源的臨界區(qū)(critical section),在這一區(qū)間內(nèi)的代碼是嚴(yán)格被Lock()保護(hù)的,是線程安全的,任何一個(gè)時(shí)間點(diǎn)都只能有一個(gè)goroutine執(zhí)行這段區(qū)間的代碼

以下是使用sync.Mutex的一個(gè)示例,稍后是非常詳細(xì)的分析過(guò)程。

package main import ( "fmt" "sync" "time" ) // 共享變量 var (    m  sync.Mutex    v1 int ) // 修改共享變量 // 在Lock()和Unlock()之間的代碼部分是臨界區(qū) func change(i int) {    m.Lock()    time.Sleep(time.Second)    v1 = v1 + 1 if v1%10 == 0 {        v1 = v1 - 10*i    }    m.Unlock()} // 訪問(wèn)共享變量 // 在Lock()和Unlock()之間的代碼部分是是臨界區(qū) func read() int {    m.Lock()    a := v1    m.Unlock() return a} func main() { var numGR = 21 var wg sync.WaitGroup    fmt.Printf("%d", read()) // 循環(huán)創(chuàng)建numGR個(gè)goroutine // 每個(gè)goroutine都執(zhí)行change()、read() // 每個(gè)change()和read()都會(huì)持有鎖 for i := 0; i < numGR; i++ {        wg.Add(1) go func(i int) { defer wg.Done()            change(i)            fmt.Printf(" -> %d", read())        }(i)    }    wg.Wait()}

第一次執(zhí)行結(jié)果:

20 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> -100 -> -99 -> -98 -> -97 -> -96 -> -95 -> -94 -> -93 -> -92 -> -91 -> -260 -> -259

第二次執(zhí)行結(jié)果:注意其中的-74和-72之間跨了一個(gè)數(shù)

20 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> -80 -> -79 -> -78 -> -77 -> -76 -> -75 -> -74 -> -72 -> -71 -> -230 -> -229 -> -229

上面的示例中,change()、read()都會(huì)申請(qǐng)鎖,并在準(zhǔn)備執(zhí)行完函數(shù)時(shí)釋放鎖,它們?nèi)绾涡薷臄?shù)據(jù)、訪問(wèn)數(shù)據(jù)本文不多做解釋。需要詳細(xì)解釋的是main()中的for循環(huán)部分。

在for循環(huán)中,會(huì)不斷激活新的goroutine(共21個(gè))執(zhí)行匿名函數(shù),在每個(gè)匿名函數(shù)中都會(huì)執(zhí)行change()和read(),意味著每個(gè)goroutine都會(huì)申請(qǐng)兩次鎖、釋放兩次鎖,且for循環(huán)中沒(méi)有任何Sleep延遲,這21個(gè)goroutine幾乎是一瞬間同時(shí)激活的。

但由于change()和read()中都申請(qǐng)鎖,對(duì)于這21個(gè)goroutine將要分別執(zhí)行的42個(gè)critical section,Lock()保證了在某一時(shí)間點(diǎn)只有其中一個(gè)goroutine能訪問(wèn)其中一個(gè)critical section。當(dāng)釋放了一個(gè)critical section,其它的Lock()將爭(zhēng)奪互斥鎖,也就是所謂的競(jìng)爭(zhēng)現(xiàn)象(race condition)。因?yàn)楦?jìng)爭(zhēng)的存在,這42個(gè)critical section被訪問(wèn)的順序是隨機(jī)的,完全無(wú)法保證哪個(gè)critical section先被訪問(wèn)。

對(duì)于前9個(gè)被調(diào)度到的goroutine,無(wú)論是哪個(gè)goroutine取得這9個(gè)change(i)中的critical section,都只是對(duì)共享變量v1做加1運(yùn)算,但當(dāng)?shù)?0個(gè)goroutine被調(diào)度時(shí),由于v1加1之后得到10,它滿足if條件,會(huì)執(zhí)行v1 = v1 - i*10,但這個(gè)i可能是任意0到numGR之間的值(因?yàn)闊o(wú)法保證并發(fā)的goroutine的調(diào)度順序),這使得v1的值從第10個(gè)goroutine開始出現(xiàn)隨機(jī)性。但從第10到第19個(gè)goroutine被調(diào)度的過(guò)程中,也只是對(duì)共享變量v1做加1運(yùn)算,這些值是可以根據(jù)第10個(gè)數(shù)推斷出來(lái)的,到第20個(gè)goroutine,又再次隨機(jī)。依此類推。

此外,每個(gè)goroutine中的read()也都會(huì)參與鎖競(jìng)爭(zhēng),所以并不能保證每次change(i)之后會(huì)隨之執(zhí)行到read(),可能goroutine 1的change()執(zhí)行完后,會(huì)跳轉(zhuǎn)到goroutine 3的change()上,這樣一來(lái),goroutine 1的read()就無(wú)法讀取到goroutine 1所修改的v1值,而是訪問(wèn)到其它goroutine中修改后的值。所以,前面的第二次執(zhí)行結(jié)果中出現(xiàn)了一次數(shù)據(jù)跨越。只不過(guò)執(zhí)行完change()后立即執(zhí)行read()的幾率比較大,所以多數(shù)時(shí)候輸出的數(shù)據(jù)都是連續(xù)的。

總而言之,Mutex保證了每個(gè)critical section安全,某一時(shí)間點(diǎn)只有一個(gè)goroutine訪問(wèn)到這部分,但也因此而出現(xiàn)了隨機(jī)性。

如果Lock()后忘記了Unlock(),將會(huì)永久阻塞而出現(xiàn)死鎖。如果

適合sync.Mutex的數(shù)據(jù)類型

其實(shí),對(duì)于內(nèi)置類型的共享變量來(lái)說(shuō),使用sync.Mutex和Lock()、Unlock()來(lái)保護(hù)也是不合理的,因?yàn)樗鼈冏陨聿话琈utex屬性。真正合理的共享變量是那些包含Mutex屬性的struct類型。例如:type mytype struct {

m   sync.Mutex var int }x := new(mytype)

這時(shí)只要想保護(hù)var變量,就先x.m.Lock(),操作完var后,再x.m.Unlock()。這樣就能保證x中的var字段變量一定是被保護(hù)的。

sync.RWMutex

Go中使用sync.RWMutex類型實(shí)現(xiàn)讀寫互斥鎖rwmutex。在源代碼的sync/rwmutex.go文件中,有如下定義:

// A RWMutex is a reader/writer mutual exclusion lock.// The lock can be held by an arbitrary number of readers or a single writer.// The zero value for a RWMutex is an unlocked mutex.//// A RWMutex must not be copied after first use.//// 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 // held if there are pending writers    writerSem   uint32 // 寫鎖需要等待讀鎖釋放的信號(hào)量    readerSem   uint32 // 讀鎖需要等待寫鎖釋放的信號(hào)量    readerCount int32  // 讀鎖后面掛起了多少個(gè)寫鎖申請(qǐng)    readerWait  int32  // 已釋放了多少個(gè)讀鎖}

上面的注釋和源代碼說(shuō)明了幾點(diǎn):

    RWMutex是基于Mutex的,在Mutex的基礎(chǔ)之上增加了讀、寫的信號(hào)量,并使用了類似引用計(jì)數(shù)的讀鎖數(shù)量
    讀鎖與讀鎖兼容,讀鎖與寫鎖互斥,寫鎖與寫鎖互斥,只有在鎖釋放后才可以繼續(xù)申請(qǐng)互斥的鎖
  1. 可以同時(shí)申請(qǐng)多個(gè)讀鎖
    有讀鎖時(shí)申請(qǐng)寫鎖將阻塞,有寫鎖時(shí)申請(qǐng)讀鎖將阻塞
    只要有寫鎖,后續(xù)申請(qǐng)讀鎖和寫鎖都將阻塞

此類型有幾個(gè)鎖和解鎖的方法:

func (rw *RWMutex) Lock() func (rw *RWMutex) RLock() func (rw *RWMutex) RLocker() Locker func (rw *RWMutex) RUnlock() func (rw *RWMutex) Unlock()

其中:

    Lock()和Unlock()用于申請(qǐng)和釋放寫鎖
    RLock()和RUnlock()用于申請(qǐng)和釋放讀鎖一次RUnlock()操作只是對(duì)讀鎖數(shù)量減1,即減少一次讀鎖的引用計(jì)數(shù)
    如果不存在寫鎖,則Unlock()引發(fā)panic,如果不存在讀鎖,則RUnlock()引發(fā)panic
    RLocker()用于返回一個(gè)實(shí)現(xiàn)了Lock()和Unlock()方法的Locker接口

此外,無(wú)論是Mutex還是RWMutex都不會(huì)和goroutine進(jìn)行關(guān)聯(lián),這意味著它們的鎖申請(qǐng)行為可以在一個(gè)goroutine中操作,釋放鎖行為可以在另一個(gè)goroutine中操作。

由于RLock()和Lock()都能保證數(shù)據(jù)不被其它goroutine修改,所以在RLock()與RUnlock()之間的,以及Lock()與Unlock()之間的代碼區(qū)都是critical section。

以下是一個(gè)示例,此示例中同時(shí)使用了Mutex和RWMutex,RWMutex用于讀、寫,Mutex只用于讀。

package main import ( "fmt" "os" "sync" "time" ) var Password = secret{password: "myPassword"}type secret struct { RWM sync.RWMutex M sync.Mutex password string} // 通過(guò)rwmutex寫 func Change(c *secret, pass string) { c.RWM.Lock()    fmt.Println("Change with rwmutex lock")    time.Sleep(3 * time.Second) c.password = pass c.RWM.Unlock()} // 通過(guò)rwmutex讀 func rwMutexShow(c *secret) string { c.RWM.RLock()    fmt.Println("show with rwmutex",time.Now().Second())    time.Sleep(1 * time.Second) defer c.RWM.RUnlock() return c.password} // 通過(guò)mutex讀,和rwMutexShow的唯一區(qū)別在于鎖的方式不同 func mutexShow(c *secret) string { c.M.Lock()    fmt.Println("show with mutex:",time.Now().Second())    time.Sleep(1 * time.Second) defer c.M.Unlock() return c.password} func main() { // 定義一個(gè)稍后用于覆蓋(重寫)的函數(shù) var show = func(c *secret) string { return "" } // 通過(guò)變量賦值的方式,選擇并重寫showFunc函數(shù) if len(os.Args) != 2 {        fmt.Println("Using sync.RWMutex!",time.Now().Second())        show = rwMutexShow    } else {        fmt.Println("Using sync.Mutex!",time.Now().Second())        show = mutexShow    } var wg sync.WaitGroup // 激活5個(gè)goroutine,每個(gè)goroutine都查看 // 根據(jù)選擇的函數(shù)不同,showFunc()加鎖的方式不同 for i := 0; i < 5; i++ {        wg.Add(1)        go func() { defer wg.Done()            fmt.Println("Go Pass:", show(&Password),time.Now().Second())        }()    } // 激活一個(gè)申請(qǐng)寫鎖的goroutine go func() {        wg.Add(1) defer wg.Done() Change(&Password, "123456")    }() // 阻塞,直到所有wg.Done wg.Wait()}

Change()函數(shù)申請(qǐng)寫鎖,并睡眠3秒后修改數(shù)據(jù),然后釋放寫鎖。

rwMutexShow()函數(shù)申請(qǐng)讀鎖,并睡眠一秒后取得數(shù)據(jù),并釋放讀鎖。注意,rwMutexShow()中的print和return是相隔一秒鐘的。

mutexShow()函數(shù)申請(qǐng)Mutex鎖,和RWMutex互不相干。和rwMutexShow()唯一不同之處在于申請(qǐng)的鎖不同。

main()中,先根據(jù)命令行參數(shù)數(shù)量決定運(yùn)行哪一個(gè)show()。之所以能根據(jù)函數(shù)變量來(lái)賦值,是因?yàn)橄榷x了一個(gè)show()函數(shù),它的函數(shù)簽名和rwMutexShow()、mutexShow()的簽名相同,所以可以相互賦值。

for循環(huán)中激活了5個(gè)goroutine并發(fā)運(yùn)行,for瞬間激活5個(gè)goroutine后,繼續(xù)執(zhí)行main()代碼會(huì)激活另一個(gè)用于申請(qǐng)寫鎖的goroutine。這6個(gè)goroutine的執(zhí)行順序是隨機(jī)的。

如果show選中的函數(shù)是rwMutexShow(),則5個(gè)goroutine要申請(qǐng)的RLock()鎖和寫鎖是沖突的,但5個(gè)RLock()是兼容的。所以,只要某個(gè)時(shí)間點(diǎn)調(diào)度到了寫鎖的goroutine,剩下的讀鎖goroutine都會(huì)從那時(shí)開始阻塞3秒。

除此之外,還有一個(gè)不嚴(yán)格準(zhǔn)確,但在時(shí)間持續(xù)長(zhǎng)短的理論上來(lái)說(shuō)能保證的一個(gè)規(guī)律:當(dāng)修改數(shù)據(jù)結(jié)束后,各個(gè)剩下的goroutine都申請(qǐng)讀鎖,因?yàn)樯暾?qǐng)后立即print輸出,然后睡眠1秒,但1秒時(shí)間足夠所有剩下的goroutine申請(qǐng)完讀鎖,使得show with rwmutex輸出是連在一起,輸出的Go Pass: 123456又是連在一起的。

某次結(jié)果如下:

Using sync.RWMutex! 58 show with rwmutex 58 Change with rwmutex lock Go Pass: myPassword 59 show with rwmutex 2 show with rwmutex 2 show with rwmutex 2 show with rwmutex 2 Go Pass: 123456 3 Go Pass: 123456 3 Go Pass: 123456 3 Go Pass: 123456 3

如果show選中的函數(shù)是mutexShow(),則讀數(shù)據(jù)和寫數(shù)據(jù)互不沖突,但讀和讀是沖突的(因?yàn)镸utex的Lock()是互斥的)。

某次結(jié)果如下:

Using sync.Mutex! 30 Change with rwmutex lock show with mutex: 30 Go Pass: myPassword 31 show with mutex: 31 Go Pass: myPassword 32 show with mutex: 32 Go Pass: 123456 33 show with mutex: 33 show with mutex: 34 Go Pass: 123456 34 Go Pass: 123456 35

用Mutex還是用RWMutex

Mutex和RWMutex都不關(guān)聯(lián)goroutine,但RWMutex顯然更適用于讀多寫少的場(chǎng)景。僅針對(duì)讀的性能來(lái)說(shuō),RWMutex要高于Mutex,因?yàn)閞wmutex的多個(gè)讀可以并存。

到此,關(guān)于“Go語(yǔ)言互斥鎖Mutex和讀寫鎖RWMutex的用法”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

本文標(biāo)題:Go語(yǔ)言互斥鎖Mutex和讀寫鎖RWMutex的用法
文章分享:http://m.rwnh.cn/article42/pdiohc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、網(wǎng)站改版網(wǎng)站維護(hù)、建站公司商城網(wǎng)站、網(wǎng)站設(shè)計(jì)公司

廣告

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

h5響應(yīng)式網(wǎng)站建設(shè)
城步| 南江县| 辽源市| 闸北区| 开化县| 龙州县| 云安县| 凉山| 荔浦县| 屏山县| 兰坪| 肃南| 邓州市| 高雄市| 封丘县| 靖州| 杨浦区| 镇巴县| 松溪县| 类乌齐县| 政和县| 蚌埠市| 西和县| 邛崃市| 大竹县| 兖州市| 平武县| 罗城| 莱芜市| 怀仁县| 潼关县| 唐河县| 永春县| 水城县| 台南市| 福海县| 突泉县| 团风县| 安康市| 望奎县| 顺平县|