GoLang中的互斥鎖Mutex和讀寫(xiě)鎖RWMutex使用教程
一、競(jìng)態(tài)條件與臨界區(qū)和同步工具
(1)競(jìng)態(tài)條件
一旦數(shù)據(jù)被多個(gè)線程共享,那么就會(huì)產(chǎn)生沖突和爭(zhēng)用的情況,這種情況被稱為競(jìng)態(tài)條件。這往往會(huì)破壞數(shù)據(jù)的一致性。
同步的用途有兩個(gè),一個(gè)是避免多線程在同一時(shí)刻操作同一個(gè)數(shù)據(jù)塊,另一個(gè)是協(xié)調(diào)多線程,以避免它們?cè)谕粫r(shí)刻執(zhí)行同一個(gè)代碼塊。
(2)臨界區(qū)
一個(gè)線程在想要訪問(wèn)某一個(gè)共享資源的時(shí)候,需要先申請(qǐng)對(duì)該資源的訪問(wèn)權(quán)限,并且只有在申請(qǐng)成功之后,訪問(wèn)才能真正開(kāi)始。
而當(dāng)線程對(duì)共享資源的訪問(wèn)結(jié)束時(shí),它還必須歸還對(duì)該資源的訪問(wèn)權(quán)限,若要再次訪問(wèn)仍需申請(qǐng)。
我們可以說(shuō),多個(gè)并發(fā)運(yùn)行的線程對(duì)這個(gè)共享資源的訪問(wèn)是完全串行的,只要一個(gè)代碼片段需要實(shí)現(xiàn)對(duì)共享資源的串行化訪問(wèn),就可以被視為一個(gè)臨界區(qū)。由于要訪問(wèn)到資源而必須進(jìn)入到那個(gè)區(qū)域。
(3)同步工具
臨界區(qū)總是受保護(hù)的,否則會(huì)產(chǎn)生競(jìng)態(tài)條件。施加保護(hù)的重要手段之一,就是使用實(shí)現(xiàn)了某種同步機(jī)制的工具,也稱為同步工具。
二、互斥量
mutual exclusion,簡(jiǎn)稱mutex
在Go語(yǔ)言中,可供我們選擇的同步工具不少。其中,最重要且最常用的同步工具當(dāng)屬互斥量(mutual exclusion,簡(jiǎn)稱mutex)。
sync包中的Mutex就是與其對(duì)應(yīng)的類型,該類型的值可以被稱為互斥量或者互斥鎖。
一個(gè)互斥鎖可以被用來(lái)保護(hù)一個(gè)臨界區(qū)或者一組臨界區(qū)。我們可以通過(guò)它來(lái)保證,在同一時(shí)刻只有一個(gè)goroutine處于該臨界區(qū)。
為了實(shí)現(xiàn)這個(gè)保證,每當(dāng)有g(shù)oroutine想進(jìn)入臨界區(qū),都要先對(duì)它進(jìn)行鎖定,并且,每個(gè)goroutine離開(kāi)臨界區(qū),都要及時(shí)對(duì)它進(jìn)行解鎖。
- 鎖定操作:調(diào)用互斥鎖的Lock方法;
- 解鎖操作:調(diào)用互斥鎖的Unlock方法;
mu.Lock() _, err := writer.Write([]byte(data)) if err != nil { log.Printf("error: %s [%d]", err, id) } mu.Unlock()
go run demo01.go -protecting=0
三、使用互斥鎖的注意事項(xiàng)
(1)使用互斥鎖的注意事項(xiàng)
使用互斥鎖的注意事項(xiàng)如下:
- 不要重復(fù)鎖定互斥鎖;
- 不要忘記解鎖互斥鎖,必要時(shí)使用defer語(yǔ)句;
- 不要對(duì)尚未鎖定或已解鎖的互斥鎖解鎖;
- 不要在多個(gè)函數(shù)之間傳遞互斥鎖;
把一個(gè)互斥鎖同時(shí)用在多個(gè)地方,不但會(huì)讓程序變慢,還會(huì)大大增加死鎖的可能性。
有GO語(yǔ)言運(yùn)行時(shí)系統(tǒng)自行拋出的panic都屬于致命錯(cuò)誤,都是無(wú)法被恢復(fù)的,調(diào)用recover函數(shù)對(duì)他們起不到任何作用。也就是說(shuō),一旦產(chǎn)生死鎖,程序必然崩潰。
為了避免這種情況,最簡(jiǎn)單有效的方法就是讓每一個(gè)互斥鎖都只保護(hù)一個(gè)臨界區(qū)或一組相關(guān)臨界區(qū)。在這個(gè)前提下,還要注意,就不要重復(fù)鎖定一個(gè)互斥鎖,也不要忘記對(duì)它的解鎖。
一個(gè)goroutine對(duì)某一個(gè)互斥鎖重復(fù)鎖定,就意味著它自己鎖死自己。
不要忘記解鎖的一個(gè)重要原因是:避免重復(fù)鎖定。
同樣,解鎖未鎖定的互斥鎖會(huì)立即引發(fā) panic。
(2)使用defer語(yǔ)句解鎖
最保險(xiǎn)的做法
如果一個(gè)流程在鎖定了某個(gè)互斥鎖之后分叉了,或者有被中斷的可能,那么就應(yīng)該使用defer語(yǔ)句來(lái)對(duì)它進(jìn)行解鎖,而且這樣的defer語(yǔ)句應(yīng)該緊跟在鎖定操作之后。這是最保險(xiǎn)的一種做法。
(3)sync.Mutex是值類型
Go 語(yǔ)言中的互斥鎖是開(kāi)箱即用的。換句話說(shuō),一旦我們聲明了一個(gè)sync.Mutex類型的變量,就可以直接使用它了。
不過(guò)要注意,該類型是一個(gè)結(jié)構(gòu)體類型,屬于值類型中的一種。把它傳給一個(gè)函數(shù)、將它從函數(shù)中返回、把它賦給其他變量、讓它進(jìn)入某個(gè)通道都會(huì)導(dǎo)致它的副本的產(chǎn)生。
并且,原值和它的副本,以及多個(gè)副本之間都是完全獨(dú)立的,它們都是不同的互斥鎖。
四、讀寫(xiě)鎖與互斥鎖的異同
(1)讀/寫(xiě)互斥鎖
讀寫(xiě)鎖是讀/寫(xiě)互斥鎖的簡(jiǎn)稱。在Go語(yǔ)言中,讀寫(xiě)鎖由sync.RWMutex
類型的值代表。與sync.Mutex
類型一樣,也是開(kāi)箱即用。
一個(gè)讀寫(xiě)鎖中,實(shí)際包含兩個(gè)鎖,即:讀鎖和寫(xiě)鎖。
sync.RWMutex類型中的Lock方法和Unlock方法分別用于對(duì)寫(xiě)鎖進(jìn)行鎖定和解鎖,而它的RLock方法和RUnlock方法則分別用于對(duì)讀鎖進(jìn)行鎖定和解鎖。
(2)讀寫(xiě)鎖規(guī)則
- 在寫(xiě)鎖已被鎖定的情況下再試圖鎖定寫(xiě)鎖,會(huì)阻塞當(dāng)前的 goroutine。
- 在寫(xiě)鎖已被鎖定的情況下試圖鎖定讀鎖,也會(huì)阻塞當(dāng)前的 goroutine。
- 在讀鎖已被鎖定的情況下試圖鎖定寫(xiě)鎖,同樣會(huì)阻塞當(dāng)前的 goroutine。
- 在讀鎖已被鎖定的情況下再試圖鎖定讀鎖,并不會(huì)阻塞當(dāng)前的 goroutine。
多個(gè)寫(xiě)操作不能同時(shí)進(jìn)行,寫(xiě)操作和讀操作也不能同時(shí)進(jìn)行,但多個(gè)讀操作卻可以同時(shí)進(jìn)行。
(3)解鎖讀寫(xiě)鎖
對(duì)寫(xiě)鎖進(jìn)行解鎖,會(huì)喚醒“所有因試圖鎖定讀鎖,而被阻塞的 goroutine”,并且,這通常會(huì)使它們都成功完成對(duì)讀鎖的鎖定。
對(duì)讀鎖進(jìn)行解鎖,只會(huì)在沒(méi)有其他讀鎖鎖定的前提下,喚醒“因試圖鎖定寫(xiě)鎖,而被阻塞的 goroutine”;并且,最終只會(huì)有一個(gè)被喚醒的 goroutine 能夠成功完成對(duì)寫(xiě)鎖的鎖定,其他的 goroutine 還要在原處繼續(xù)等待。至于是哪一個(gè) goroutine,那就要看誰(shuí)的等待時(shí)間最長(zhǎng)了。
與互斥鎖類似,解鎖“讀寫(xiě)鎖中未被鎖定的寫(xiě)鎖”,會(huì)立即引發(fā) panic,對(duì)于其中的讀鎖也是如此,并且同樣是不可恢復(fù)的。,與互斥鎖類似,解鎖“讀寫(xiě)鎖中未被鎖定的寫(xiě)鎖”,會(huì)立即引發(fā) panic,對(duì)于其中的讀鎖也是如此,并且同樣是不可恢復(fù)的。
到此這篇關(guān)于GoLang中的互斥鎖Mutex和讀寫(xiě)鎖RWMutex使用教程的文章就介紹到這了,更多相關(guān)GoLang Mutex和RWMutex內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang學(xué)習(xí)筆記(一):簡(jiǎn)介
這篇文章主要介紹了Golang學(xué)習(xí)筆記(一):簡(jiǎn)介,本文講解了Go語(yǔ)言最主要的特性、安裝、環(huán)境變量設(shè)置、整體目錄結(jié)構(gòu)、Helloworld、go命令、調(diào)試、編輯器設(shè)置等內(nèi)容,需要的朋友可以參考下2015-05-05Go語(yǔ)言中strings和strconv包示例代碼詳解
這篇文章主要介紹了Go語(yǔ)言中strings和strconv包示例代碼詳解 ,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11Go爬蟲(chóng)(http、goquery和colly)詳解
goquery可以避免操作復(fù)雜的正則表達(dá)式,它可以直接根據(jù)url獲取一個(gè)Document對(duì)象,然后根據(jù)標(biāo)簽選擇器、類選擇器和id選擇器獲取相應(yīng)的選擇對(duì)象,進(jìn)行自定義的操作,這篇文章主要介紹了Go爬蟲(chóng)(http、goquery和colly),需要的朋友可以參考下2022-09-09一文深入探索Go語(yǔ)言中的循環(huán)結(jié)構(gòu)
在編程中,循環(huán)結(jié)構(gòu)扮演著重要的角色,它使我們能夠有效地重復(fù)執(zhí)行特定的代碼塊,以實(shí)現(xiàn)各種任務(wù)和邏輯,在Go語(yǔ)言中,for 是 Go 中唯一的循環(huán)結(jié)構(gòu),本文將深入探討Go語(yǔ)言中的for循環(huán)類型以及它們的用法2023-08-08gtoken替換jwt實(shí)現(xiàn)sso登錄的問(wèn)題小結(jié)
這篇文章主要介紹了gtoken替換jwt實(shí)現(xiàn)sso登錄,主要介紹了替換jwt的原因分析及gtoken的優(yōu)勢(shì),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05Go中RPC遠(yuǎn)程過(guò)程調(diào)用的實(shí)現(xiàn)
本文主要介紹了Go中RPC遠(yuǎn)程過(guò)程調(diào)用的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Golang開(kāi)發(fā)之字符串與切片問(wèn)題踩坑記錄
字符串和切片,都是golang常用的兩種內(nèi)置數(shù)據(jù)類型,最近在日常工作中,遇到了一個(gè)字符串切片導(dǎo)致的問(wèn)題,記錄一下排查問(wèn)題的過(guò)程,避免后續(xù)在這種場(chǎng)景上踩坑2023-07-07Golang的os標(biāo)準(zhǔn)庫(kù)中常用函數(shù)的整理介紹
這篇文章主要介紹了Go語(yǔ)言的os標(biāo)準(zhǔn)庫(kù)中常用函數(shù),主要用來(lái)實(shí)現(xiàn)與操作系統(tǒng)的交互功能,需要的朋友可以參考下2015-10-10