網站首頁 編程語言 正文
資源競爭
channel 常用于并發通信,要保證并發安全,主要使用互斥鎖。在并發的過程中,當一個內存被多個 goroutine 同時訪問時,就會產生資源競爭的情況。這塊內存也可以稱為共享資源。
并發時對于共享資源必然會出現搶占資源的情況,如果是對某資源的統計,很可能就會導致結果錯誤。為保證只有一個協程拿到資源并操作它,可以引入互斥鎖 sync.Mutex。
sync.Mutex
互斥鎖,指的是并發時,在同一時刻只有一個協程執行某段代碼,其他協程只有等待該協程執行完成后才能繼續執行。
var (sum int mutex sync.Mutex) func add(i int){ mutex.Lock() sum+=i mute.Unlock() }
使用 mutex 加鎖保護 sum+ =i 的代碼片段,這樣這個片段區就無法同時被多個協程訪問,當有協程進入該片段區,那其他的協程就只有等待,以此保證臨界區的并發安全。
sync.Mutex 只有 Lock()和 Unlock() 方法,它們是成對存在的,且Lock后一定要執行Unlock進行釋放鎖。所以可以使用 defer 語句釋放鎖,以保證鎖一定會被釋放。
func add(i int){ mutex.Lock() defer mutex.Unlock() sum += i }
sync.RWMutex
上面例子是對 sum 寫操作時使用sync.Mutex 保證并發安全,當多個協程進行讀取操作時,避免因并發產生讀取數據不正確,也是可以使用互斥鎖 sync.Mutex。
func getSum(){ mutex.Lock() defer mutex.Unlock() b:=sum return b }
多個協程 goroutine 同時讀寫的資源競爭問題解決,還需要考慮性能問題,每次讀寫共享資源都加鎖,也會導致性能低。
多個協程并發進行讀寫時會遇到以下情況:
- 寫時不能同時讀,易讀到臟數據
- 讀時不能同時寫,因為會導致結果不一致
- 讀時同時寫,因數據不變,無論多少個 goroutine 讀都是并發安全
使用讀寫鎖 sync.RWMutex 優化代碼:
var mutex sync.RWMutex func readSum() int { mutex.RLock() defer mutex.RUnlock() b := sum return b }
讀寫鎖的性能比互斥鎖性能好。
sync.WaitGroup
為了能夠監聽所有的協程的執行,一旦所有的goroutine 都執行完成,程序應當及時退出節省時間提高性能。通過使用 sync.WaitGroup 來解決。使用步驟如下:
- 聲明一個 sync.WaitGroup ,通過 add 方法增加計數器值,有幾個協程就計算幾個
- 每個協程結束后就調用 Done 方法,主要是讓計數器減1
- 最后調用 Wait 方法一直等待,直到計數器為 0 時,所有跟蹤的協程都執行完畢
func run() { var wg sync.WaitGroup wg.Add(100) for i := 0; i < 100; i++ { go func() { defer wg.Done() add(10) }() } wg.Wait() }
通過 sync.WaitGroup 可以很好地跟蹤協程.
sync.Once
sync.Once 作用是讓代碼只執行一次。詳細使用是調用方法 once.Do 方法,具體實現:
func main(){ var once sync.once oneFunc := func(){ println("once func") } once.Do(oneFunc) }
sync.Once 適用于創建某個對象的單例、只加載一次的資源等只執行一次的場景。
sync.Cond
使用 sync.WaitGroup 主要是控制等待所有的協程都執行完畢,才最終完成。但是當遇到場景是,只有等待所有條件都準備好才開始。sync.Cond 相當于發號施令,只有通知執行所有的協程才可以執行,重點是所有協程需等待喚醒才可以開始。
所以 sync.Cond 具有阻塞協程和喚醒協程的功能。詳細的用法:
- 通過 sync.NewCond 函數生成一個 *sync.Cond,用于阻塞和喚醒協程
- 調用 cond.Wait() 方法阻塞當前協程等待,需要注意調用 cond.Wait() 方法要加鎖
- 調用 cond.Broadcast() 后所有協程才開始執行
func run() { cond := sync.NewCond(&sync.Mutex{}) var wg sync.WaitGroup wg.Add(101) for i := 0; i < 100; i++ { go func(num int) { defer wg.Done() fmt.Println(num, "號正在 awaiting......") cond.L.Lock() cond.Wait() //等待所有協程準備完成 fmt.Println(num, "號開始跑……") cond.L.Unlock() }(i) } // 等待所有的協程都進入 wait 狀態 time.Sleep(2*time.Second) go func() { defer wg.Done() // 所有都準備完成,開始 cond.Broadcast() }() // 防止函數提前返回退出 wg.Wait() }
原文鏈接:https://juejin.cn/post/7127846135917445127
相關推薦
- 2022-04-12 python入門之scrapy框架中Request對象和Response對象的介紹_python
- 2023-01-30 PyQt中使用QProcess運行一個進程的示例代碼_python
- 2022-04-22 Golang執行流程詳解,兩種執行流程方式有什么不同
- 2022-09-25 線性回歸的從零開始實現(線性神經網絡)
- 2022-02-12 uni-app 自定義導航欄 圖片按鈕
- 2022-06-28 python神經網絡學習使用Keras進行回歸運算_python
- 2022-04-01 本地存儲(Local Storage) 和 會話存儲(Session Storage)
- 2022-12-06 Android?Doze模式下Alarm定時任務實現流程詳解_Android
- 最近更新
-
- 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同步修改后的遠程分支