網站首頁 編程語言 正文
并發安全,就是多個并發體在同一段時間內訪問同一個共享數據,共享數據能被正確處理。
很多語言的并發編程很容易在同時修改某個變量的時候,因為操作不是原子的,而出現錯誤計算,比如一個加法運算使用中的變量被修改,而導致計算結果出錯,典型的像統計商品庫存。
個人建議只要涉及到共享變量統統使用channel
,因為channel
源碼中使用了互斥鎖,它是并發安全的。
我們可以不用,但不可以不了解,手中有糧心中不慌。
并發不安全的例子
數組是并發不安全的,在例子開始前我們要知道append
函數的行為:長度足夠在原數組cap
內追加函數,增加len
,長度不夠則觸發擴容,申請新數組cap
增加一倍,賦值遷移。
所以在這個過程中,僅討論擴容操作的話可能存在同時申請并賦值的情況,導致漏掉某次擴容增加的數據。
var s []int func appendValue(i int) { s = append(s, i) } func main() { for i := 0; i < 10000; i++ { //10000個協程同時添加切片 go appendValue(i) } time.Sleep(2) fmt.Println(len(s)) }
比如上例,10000
個協程同時為切片增加數據,你可以嘗試運行一下,打印出來的一定不是 10000
。
- 以上并發操作的同一個資源,專業名詞叫做臨界區。
- 因為并發操作存在數據競爭,導致數據值意外改寫,最后的結果與期待的不符,這種問題統稱為競態問題。
常見于控制商品減庫存,控制余額增減等情況。 那么有什么辦法解決競態問題呢?
- 互斥鎖:讓訪問某個臨界區的時候,只有一個
goroutine
可以訪問。 - 原子操作:讓某些操作變成原子的,這個后續討論。
這兩個思路貫穿了無數的高并發/分布式方案,區別是在一個進程應用中使用,還是借助某些第三方手段來實現,比如中間件。獨孤九劍森羅萬象一定要牢牢記住。
互斥鎖
Go
語言中互斥鎖的用法如下:
var lock sync.Mutex //互斥鎖 lock.Lock() //加鎖 s = append(s, i) lock.Unlock() //解鎖
在訪問臨界區的前后加上互斥鎖,就可以保證不會出現并發問題。
我們修改還是上一個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個協程同時添加切片 go appendValueSafe(i) } time.Sleep(2) fmt.Println(len(s))
- 對共享變量
s
的寫入操作加互斥鎖,保證同一時刻只有一個goroutine
修改內容。 - 加鎖之后到解鎖之前的內容,同一時刻只有一個訪問,無論讀寫。
- 無論多少次都輸出
10000
,不會再出現競態問題。 - 要注意:如果在寫的同時,有并發讀操作時,為了防止不要讀取到寫了一半數據,需要為讀操作也加鎖。
讀寫鎖
互斥鎖是完全互斥的,并發讀沒有修改的情況下是不會有問題的,也沒有必要在并發讀的時候加鎖不然效率會變低。
用法:
rwlock sync.RWMutex //讀鎖 rwlock.RLock() rwlock.RUnlock() //寫鎖 rwlock.Lock() rwlock.Unlock()
并發讀不互斥可以同時,在一個寫鎖獲取時,其他所有鎖都等待, 口訣:讀讀不互斥、讀寫互斥、寫寫互斥。具體測算速度的代碼可以見4.7.3的源碼,感興趣的可以改下注釋位置看下效率是有很明顯的提升的。
小結
- 學習了幾個名詞:臨界區、競態問題、互斥鎖、原子操作、讀寫鎖。
- 互斥鎖:
sync.Mutex
, 讀寫鎖:sync.RWMutex
都是sync
包的。 - 讀寫鎖比互斥鎖效率高。
問題:只加寫鎖可以嗎?為什么?
總結
原文鏈接:https://juejin.cn/post/7181490237439213629
- 上一篇:C語言求質數的幾種簡單易懂方式_C 語言
- 下一篇:C語言如何實現三子棋_C 語言
相關推薦
- 2022-12-05 Windows的sc命令詳解(sc命令用法)_DOS/BAT
- 2022-06-06 uniApp、API ‘offCompassChange‘ is not yet implement
- 2022-04-12 Apache?Pulsar集群搭建部署詳細過程_Linux
- 2022-04-09 在Mybatis中使用自定義緩存ehcache
- 2022-12-27 Android?Compose狀態改變動畫animateXxxAsState使用詳解_Android
- 2022-09-16 C語言庫函數getchar()新見解_C 語言
- 2022-03-05 CentOS系統下安裝及配置JDK介紹_Linux
- 2022-05-15 Pycharm創建Django項目示例實踐_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支