golang并發(fā)安全及讀寫(xiě)互斥鎖的示例分析
并發(fā)安全和鎖
有時(shí)候在Go代碼中可能會(huì)存在多個(gè)goroutine同時(shí)操作一個(gè)資源(臨界區(qū)),這種情況會(huì)發(fā)生競(jìng)態(tài)問(wèn)題(數(shù)據(jù)競(jìng)態(tài))。類比現(xiàn)實(shí)生活中的例子有十字路口被各個(gè)方向的的汽車(chē)競(jìng)爭(zhēng);還有火車(chē)上的衛(wèi)生間被車(chē)廂里的人競(jìng)爭(zhēng)。
舉個(gè)例子:
var x int64
var wg sync.WaitGroup
func add() {
for i := 0; i < 5000; i++ {
x = x + 1
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
上面的代碼中我們開(kāi)啟了兩個(gè)goroutine去累加變量x的值,這兩個(gè)goroutine在訪問(wèn)和修改x變量的時(shí)候就會(huì)存在數(shù)據(jù)競(jìng)爭(zhēng),導(dǎo)致最后的結(jié)果與期待的不符。
互斥鎖
import ( "fmt" "sync" ) var lock sync.Mutex lock.Lock() // 加鎖 lock.Unlock() // 解鎖
互斥鎖是一種常用的控制共享資源訪問(wèn)的方法,它能夠保證同時(shí)只有一個(gè)goroutine可以訪問(wèn)共享資源。Go語(yǔ)言中使用sync包的Mutex類型來(lái)實(shí)現(xiàn)互斥鎖。 使用互斥鎖來(lái)修復(fù)上面代碼的問(wèn)題:
var x int64
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
for i := 0; i < 5000; i++ {
lock.Lock() // 加鎖
x = x + 1
lock.Unlock() // 解鎖
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println(x)
}
使用互斥鎖能夠保證同一時(shí)間有且只有一個(gè)goroutine進(jìn)入臨界區(qū),其他的goroutine則在等待鎖;當(dāng)互斥鎖釋放后,等待的goroutine才可以獲取鎖進(jìn)入臨界區(qū),多個(gè)goroutine同時(shí)等待一個(gè)鎖時(shí),喚醒的策略是隨機(jī)的。
讀寫(xiě)互斥鎖
import ( "fmt" "sync" ) var rwlock sync.RWMutex rwlock.Lock() // 加寫(xiě)鎖 rwlock.Unlock() // 解寫(xiě)鎖
互斥鎖是完全互斥的,但是有很多實(shí)際的場(chǎng)景下是讀多寫(xiě)少的,當(dāng)我們并發(fā)的去讀取一個(gè)資源不涉及資源修改的時(shí)候是沒(méi)有必要加鎖的,這種場(chǎng)景下使用讀寫(xiě)鎖是更好的一種選擇。讀寫(xiě)鎖在Go語(yǔ)言中使用sync包中的RWMutex類型。
讀寫(xiě)鎖分為兩種:讀鎖和寫(xiě)鎖。當(dāng)一個(gè)goroutine獲取讀鎖之后,其他的goroutine如果是獲取讀鎖會(huì)繼續(xù)獲得鎖,如果是獲取寫(xiě)鎖就會(huì)等待;當(dāng)一個(gè)goroutine獲取寫(xiě)鎖之后,其他的goroutine無(wú)論是獲取讀鎖還是寫(xiě)鎖都會(huì)等待。
讀寫(xiě)鎖示例:
var (
x int64
wg sync.WaitGroup
lock sync.Mutex
rwlock sync.RWMutex
)
func write() {
// lock.Lock() // 加互斥鎖
rwlock.Lock() // 加寫(xiě)鎖
x = x + 1
time.Sleep(10 * time.Millisecond) // 假設(shè)讀操作耗時(shí)10毫秒
rwlock.Unlock() // 解寫(xiě)鎖
// lock.Unlock() // 解互斥鎖
wg.Done()
}
func read() {
// lock.Lock() // 加互斥鎖
rwlock.RLock() // 加讀鎖
time.Sleep(time.Millisecond) // 假設(shè)讀操作耗時(shí)1毫秒
rwlock.RUnlock() // 解讀鎖
// lock.Unlock() // 解互斥鎖
wg.Done()
}
func main() {
start := time.Now()
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
}
for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
}
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start))
}
需要注意的是讀寫(xiě)鎖非常適合讀多寫(xiě)少的場(chǎng)景,如果讀和寫(xiě)的操作差別不大,讀寫(xiě)鎖的優(yōu)勢(shì)就發(fā)揮不出來(lái)。
以上就是golang并發(fā)安全及讀寫(xiě)互斥鎖的示例分析的詳細(xì)內(nèi)容,更多關(guān)于golang并發(fā)安全及讀寫(xiě)互斥鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
GO語(yǔ)言獲取系統(tǒng)環(huán)境變量的方法
這篇文章主要介紹了GO語(yǔ)言獲取系統(tǒng)環(huán)境變量的方法,實(shí)例分析了Getenv方法操作環(huán)境變量的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02
Golang算法問(wèn)題之整數(shù)拆分實(shí)現(xiàn)方法分析
這篇文章主要介紹了Golang算法問(wèn)題之整數(shù)拆分實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Go語(yǔ)言數(shù)值運(yùn)算與數(shù)組遍歷相關(guān)操作技巧,需要的朋友可以參考下2017-02-02
Go語(yǔ)言中http和mysql的實(shí)現(xiàn)代碼
本文通過(guò)實(shí)例代碼給大家介紹了Go語(yǔ)言中http和mysql的實(shí)現(xiàn)代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11
如何使用Golang發(fā)送Get和Post請(qǐng)求
這篇文章主要給大家介紹了關(guān)于如何使用Golang發(fā)送Get和Post請(qǐng)求的相關(guān)資料,Go語(yǔ)言(Golang)的標(biāo)準(zhǔn)庫(kù)提供了處理HTTP請(qǐng)求的功能,這使得將Go用于web應(yīng)用程序變得非常容易,需要的朋友可以參考下2023-06-06
一文帶你了解Go語(yǔ)言中鎖特性和實(shí)現(xiàn)
Go語(yǔ)言中的sync包主要提供的對(duì)并發(fā)操作的支持,標(biāo)志性的工具有cond(條件變量)?once?(原子性)?還有?鎖,本文會(huì)主要向大家介紹Go語(yǔ)言中鎖的特性和實(shí)現(xiàn),感興趣的可以了解下2024-03-03
在Linux系統(tǒng)中安裝Go語(yǔ)言的詳細(xì)教程
這篇文章主要介紹了在Linux系統(tǒng)中安裝Go語(yǔ)言的詳細(xì)教程,由于國(guó)內(nèi)很多人對(duì)谷歌的盲目追捧,導(dǎo)致Go語(yǔ)言在國(guó)內(nèi)的人氣遠(yuǎn)超國(guó)外...需要的朋友可以參考下2015-06-06

