日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

Go并發(fā)與鎖的兩種方式該如何提效詳解_Golang

作者:機(jī)智的程序員小熊 ? 更新時(shí)間: 2023-02-02 編程語言

并發(fā)安全,就是多個(gè)并發(fā)體在同一段時(shí)間內(nèi)訪問同一個(gè)共享數(shù)據(jù),共享數(shù)據(jù)能被正確處理。

很多語言的并發(fā)編程很容易在同時(shí)修改某個(gè)變量的時(shí)候,因?yàn)椴僮鞑皇窃拥模霈F(xiàn)錯(cuò)誤計(jì)算,比如一個(gè)加法運(yùn)算使用中的變量被修改,而導(dǎo)致計(jì)算結(jié)果出錯(cuò),典型的像統(tǒng)計(jì)商品庫存。

個(gè)人建議只要涉及到共享變量統(tǒng)統(tǒng)使用channel,因?yàn)?code>channel源碼中使用了互斥鎖,它是并發(fā)安全的。

我們可以不用,但不可以不了解,手中有糧心中不慌。

并發(fā)不安全的例子

數(shù)組是并發(fā)不安全的,在例子開始前我們要知道append函數(shù)的行為:長度足夠在原數(shù)組cap內(nèi)追加函數(shù),增加len,長度不夠則觸發(fā)擴(kuò)容,申請(qǐng)新數(shù)組cap增加一倍,賦值遷移。

所以在這個(gè)過程中,僅討論擴(kuò)容操作的話可能存在同時(shí)申請(qǐng)并賦值的情況,導(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個(gè)協(xié)程同時(shí)添加切片
		go appendValue(i)
	}
    time.Sleep(2)
    fmt.Println(len(s))
}

比如上例,10000 個(gè)協(xié)程同時(shí)為切片增加數(shù)據(jù),你可以嘗試運(yùn)行一下,打印出來的一定不是 10000

  • 以上并發(fā)操作的同一個(gè)資源,專業(yè)名詞叫做臨界區(qū)
  • 因?yàn)椴l(fā)操作存在數(shù)據(jù)競爭,導(dǎo)致數(shù)據(jù)值意外改寫,最后的結(jié)果與期待的不符,這種問題統(tǒng)稱為競態(tài)問題

常見于控制商品減庫存,控制余額增減等情況。 那么有什么辦法解決競態(tài)問題呢?

  • 互斥鎖:讓訪問某個(gè)臨界區(qū)的時(shí)候,只有一個(gè) goroutine 可以訪問。
  • 原子操作:讓某些操作變成原子的,這個(gè)后續(xù)討論。

這兩個(gè)思路貫穿了無數(shù)的高并發(fā)/分布式方案,區(qū)別是在一個(gè)進(jìn)程應(yīng)用中使用,還是借助某些第三方手段來實(shí)現(xiàn),比如中間件。獨(dú)孤九劍森羅萬象一定要牢牢記住。

互斥鎖

Go 語言中互斥鎖的用法如下:

var lock sync.Mutex //互斥鎖
lock.Lock() //加鎖
s = append(s, i)
lock.Unlock() //解鎖

在訪問臨界區(qū)的前后加上互斥鎖,就可以保證不會(huì)出現(xiàn)并發(fā)問題。

我們修改還是上一個(gè)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個(gè)協(xié)程同時(shí)添加切片
    go appendValueSafe(i)
}
time.Sleep(2)
fmt.Println(len(s))
  • 對(duì)共享變量s的寫入操作加互斥鎖,保證同一時(shí)刻只有一個(gè)goroutine修改內(nèi)容。
  • 加鎖之后到解鎖之前的內(nèi)容,同一時(shí)刻只有一個(gè)訪問,無論讀寫。
  • 無論多少次都輸出10000,不會(huì)再出現(xiàn)競態(tài)問題。
  • 要注意:如果在寫的同時(shí),有并發(fā)讀操作時(shí),為了防止不要讀取到寫了一半數(shù)據(jù),需要為讀操作也加鎖。

讀寫鎖

互斥鎖是完全互斥的,并發(fā)讀沒有修改的情況下是不會(huì)有問題的,也沒有必要在并發(fā)讀的時(shí)候加鎖不然效率會(huì)變低。

用法:

rwlock sync.RWMutex
//讀鎖
rwlock.RLock()
rwlock.RUnlock()

//寫鎖
rwlock.Lock()
rwlock.Unlock()

并發(fā)讀不互斥可以同時(shí),在一個(gè)寫鎖獲取時(shí),其他所有鎖都等待, 口訣:讀讀不互斥、讀寫互斥、寫寫互斥。具體測(cè)算速度的代碼可以見4.7.3的源碼,感興趣的可以改下注釋位置看下效率是有很明顯的提升的。

小結(jié)

  • 學(xué)習(xí)了幾個(gè)名詞:臨界區(qū)、競態(tài)問題、互斥鎖、原子操作、讀寫鎖。
  • 互斥鎖:sync.Mutex, 讀寫鎖:sync.RWMutex 都是 sync 包的。
  • 讀寫鎖比互斥鎖效率高。

問題:只加寫鎖可以嗎?為什么?

總結(jié)

原文鏈接:https://juejin.cn/post/7181490237439213629

欄目分類
最近更新