Go并發(fā)與鎖的兩種方式該如何提效詳解
并發(fā)安全,就是多個并發(fā)體在同一段時間內(nèi)訪問同一個共享數(shù)據(jù),共享數(shù)據(jù)能被正確處理。
很多語言的并發(fā)編程很容易在同時修改某個變量的時候,因為操作不是原子的,而出現(xiàn)錯誤計算,比如一個加法運(yùn)算使用中的變量被修改,而導(dǎo)致計算結(jié)果出錯,典型的像統(tǒng)計商品庫存。
個人建議只要涉及到共享變量統(tǒng)統(tǒng)使用channel,因為channel源碼中使用了互斥鎖,它是并發(fā)安全的。
我們可以不用,但不可以不了解,手中有糧心中不慌。
并發(fā)不安全的例子
數(shù)組是并發(fā)不安全的,在例子開始前我們要知道append函數(shù)的行為:長度足夠在原數(shù)組cap內(nèi)追加函數(shù),增加len,長度不夠則觸發(fā)擴(kuò)容,申請新數(shù)組cap增加一倍,賦值遷移。
所以在這個過程中,僅討論擴(kuò)容操作的話可能存在同時申請并賦值的情況,導(dǎo)致漏掉某次擴(kuò)容增加的數(shù)據(jù)。
var s []int
func appendValue(i int) {
s = append(s, i)
}
func main() {
for i := 0; i < 10000; i++ { //10000個協(xié)程同時添加切片
go appendValue(i)
}
time.Sleep(2)
fmt.Println(len(s))
}比如上例,10000 個協(xié)程同時為切片增加數(shù)據(jù),你可以嘗試運(yùn)行一下,打印出來的一定不是 10000 。
- 以上并發(fā)操作的同一個資源,專業(yè)名詞叫做臨界區(qū)。
- 因為并發(fā)操作存在數(shù)據(jù)競爭,導(dǎo)致數(shù)據(jù)值意外改寫,最后的結(jié)果與期待的不符,這種問題統(tǒng)稱為競態(tài)問題。
常見于控制商品減庫存,控制余額增減等情況。 那么有什么辦法解決競態(tài)問題呢?
- 互斥鎖:讓訪問某個臨界區(qū)的時候,只有一個
goroutine可以訪問。 - 原子操作:讓某些操作變成原子的,這個后續(xù)討論。
這兩個思路貫穿了無數(shù)的高并發(fā)/分布式方案,區(qū)別是在一個進(jìn)程應(yīng)用中使用,還是借助某些第三方手段來實現(xiàn),比如中間件。獨孤九劍森羅萬象一定要牢牢記住。
互斥鎖
Go 語言中互斥鎖的用法如下:
var lock sync.Mutex //互斥鎖 lock.Lock() //加鎖 s = append(s, i) lock.Unlock() //解鎖
在訪問臨界區(qū)的前后加上互斥鎖,就可以保證不會出現(xiàn)并發(fā)問題。
我們修改還是上一個4.7.1的例子,為其增加互斥鎖。
var s []int
var lock sync.Mutex
appendValueSafe := func(i int) {
lock.Lock()
s = append(s, i)
lock.Unlock()
}
for i := 0; i < 10000; i++ { //10000個協(xié)程同時添加切片
go appendValueSafe(i)
}
time.Sleep(2)
fmt.Println(len(s))- 對共享變量
s的寫入操作加互斥鎖,保證同一時刻只有一個goroutine修改內(nèi)容。 - 加鎖之后到解鎖之前的內(nèi)容,同一時刻只有一個訪問,無論讀寫。
- 無論多少次都輸出
10000,不會再出現(xiàn)競態(tài)問題。 - 要注意:如果在寫的同時,有并發(fā)讀操作時,為了防止不要讀取到寫了一半數(shù)據(jù),需要為讀操作也加鎖。
讀寫鎖
互斥鎖是完全互斥的,并發(fā)讀沒有修改的情況下是不會有問題的,也沒有必要在并發(fā)讀的時候加鎖不然效率會變低。
用法:
rwlock sync.RWMutex //讀鎖 rwlock.RLock() rwlock.RUnlock() //寫鎖 rwlock.Lock() rwlock.Unlock()
并發(fā)讀不互斥可以同時,在一個寫鎖獲取時,其他所有鎖都等待, 口訣:讀讀不互斥、讀寫互斥、寫寫互斥。具體測算速度的代碼可以見4.7.3的源碼,感興趣的可以改下注釋位置看下效率是有很明顯的提升的。
小結(jié)
- 學(xué)習(xí)了幾個名詞:臨界區(qū)、競態(tài)問題、互斥鎖、原子操作、讀寫鎖。
- 互斥鎖:
sync.Mutex, 讀寫鎖:sync.RWMutex都是sync包的。 - 讀寫鎖比互斥鎖效率高。
問題:只加寫鎖可以嗎?為什么?
總結(jié)
到此這篇關(guān)于Go并發(fā)與鎖的兩種方式該如何提效的文章就介紹到這了,更多相關(guān)Go并發(fā)與鎖提效內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go Slice進(jìn)行參數(shù)傳遞如何實現(xiàn)詳解
這篇文章主要為大家介紹了Go Slice進(jìn)行參數(shù)傳遞如何實現(xiàn)的過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Go語言關(guān)于幾種深度拷貝(deepcopy)方法的性能對比
這篇文章主要介紹了Go語言關(guān)于幾種深度拷貝(deepcopy)方法的性能對比,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
手把手教你如何在Goland中創(chuàng)建和運(yùn)行項目
歡迎來到本指南!我們將手把手地教您在Goland中如何創(chuàng)建、配置并運(yùn)行項目,通過簡單的步驟,您將迅速上手這款強(qiáng)大的集成開發(fā)環(huán)境(IDE),輕松實現(xiàn)您的編程夢想,讓我們一起開啟這段精彩的旅程吧!2024-02-02

