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