網(wǎng)站首頁 編程語言 正文
并發(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
相關(guān)推薦
- 2022-08-16 Go單元測(cè)試工具gomonkey的使用_Golang
- 2022-05-14 聊聊python?邏輯運(yùn)算及奇怪的返回值(not,and,or)問題_python
- 2022-10-10 python正則表達(dá)式之re.match()與re.search()的用法及區(qū)別_python
- 2022-10-03 github訪問速度慢的問題完美解決_相關(guān)技巧
- 2022-09-27 linux?shell中Grep命令查找多個(gè)字符串(grep同時(shí)匹配多個(gè)關(guān)鍵字或任意關(guān)鍵字)_lin
- 2022-05-27 C++?動(dòng)態(tài)規(guī)劃算法使用分析_C 語言
- 2022-08-05 Android自定義Span實(shí)現(xiàn)文字漸變效果_Android
- 2022-03-17 C#表達(dá)式樹Expression動(dòng)態(tài)創(chuàng)建表達(dá)式_C#教程
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支