golang 多协程的同步方法总结
之前用 go 寫一個小工具的時候, 用到了多個協程之間的通信, 當時隨手查了查, 結果查出來一大坨, 簡單記錄一下. golang中多個協程之間是如何進行通信及數據同步的嘞.
共享變量
一個最簡單, 最容易想到的, 就是通過全局變量的方式, 多個協程讀寫同一個變量. 但對同一個變量的更改, 就不得不加鎖了, 否則極易引發數據問題. 一般系統庫都提供基本的鎖, go 也提供了.
package mainimport ("fmt""sync""time" )var num = 0 // 互斥鎖 var mutex = sync.Mutex{} // 讀寫鎖 var rwMutex = sync.RWMutex{}func main() {for i := 0; i < 100; i++ {go incrNum()}time.Sleep(2)fmt.Println(num) }func incrNum() {mutex.Lock()num = num + 1mutex.Unlock() }僅執行一次
當查詢鎖查到sync這個模塊時, 發現它下面的對象并沒有幾個, 都是針對協程同步的各個方面給出的解決方案. 所以我就一個一個看文檔試了試.
當你需要對環境, 連接池等等資源進行初始化時, 這種操作只需要執行一次, 這時候就需要它了. sync.Once對象可以保證僅執行一次. 和 init 方法有些類似, 不過 init 方法是在模塊首次加載時執行, 而sync.Once是在首次調用時執行. (其實現就是一個計數器加一個互斥鎖)
package mainimport ("fmt""sync""time" )var num = 0 var once = sync.Once{}func main() {for i := 0; i < 100; i++ {go once.Do(incrNum)}time.Sleep(2)fmt.Println(num) }func incrNum() {num = num + 1 }等待其他協程處理
某個協程需要等第一階段的所有協程處理完畢, 才能開始執行第二階段. 這個時候, 等待其他協程就可以通過sync.WaitGroup 來實現. (當然, 也可以通過一個共享計數器變量來實現).
package mainimport ("fmt""sync" )var waitGroup = sync.WaitGroup{}func main() {for i := 0; i < 100; i++ {go incrNum()}// 等待其他協程處理完畢(共享變量為0)waitGroup.Wait()fmt.Println("don") }func incrNum() {// 增加需要等待的協程數量(共享變量+1)waitGroup.Add(1)// do something// 標記當前協程處理完成(共享變量-1)waitGroup.Done() }消息通知
多個協程啟動時, 等待某個命令到來時執行命令, 喚醒等待協程. go 對此類操作也進行了處理, 感覺好貼心哦. 但是經過測試, 即使沒有空閑的協程, 喚醒命令同樣能夠發出去, 所以需要注意一下.
package mainimport ("sync" )var mutex = &sync.Mutex{} var cond = sync.NewCond(mutex)func main() {for i := 0; i < 100; i++ {go incrNum()}// 發送命令給一個隨機獲得鎖的協程cond.Signal()// 發送命令給所有獲得鎖的協程cond.Broadcast() }func incrNum() {// 獲取鎖, 標識當前協程可以處理命令cond.L.Lock()// 可添加退出執行命令隊列的條件for true {// 等待命令cond.Wait()// do something}// 釋放鎖, 標記命令處理完畢, 退出協程cond.L.Unlock() }多協程 map
普通的 map 在多協程操作時, 是不支持并發寫入的. go貼心的給封裝了支持并發寫入的map. 同時也提供了針對map的基本操作.
package mainimport ("fmt""sync""time" )var m = sync.Map{}func main() {for i := 0; i < 100; i++ {go func() {m.Store("1", 1)}()}time.Sleep(time.Second * 2)// 遍歷 mapm.Range(func(key, value interface{}) bool {// 返回 false 結束遍歷return true})// 讀取變量, 若不存在則設置m.LoadOrStore("1", 3)// 刪除 keym.Delete("1")// 讀取變量load, _ := m.Load("1")fmt.Println(load) }多協程對象池
對于數據庫連接池應該并不陌生. 而sync.Pool對象是go封裝的協程安全的對象池. 對象池的使用十分簡單, 存/取
package mainimport ("sync" )var p = sync.Pool{// 當池子中沒有對象了, 用于創建新對象New: func() interface {}{return "3"}, }func main() {// 從池子中獲取一個對象r := p.Get()// 用完后將對象放回池子中p.Put(r) }sync 簡單總結
針對go系統的sync模塊, 提供的基礎功能如下:
幾個都簡單試過之后, 發現sync模塊針對常用的幾個多協程工具進行了封裝, 想來可以基本滿足日常使用了.
終極通信-channel
channel是一個協程安全的通信管道, 簡單理解為數據從一側放入, 從另一側拿出. 這玩意感覺能玩出花來, 還不太理解, 留到國慶研究.
總結
以上是生活随笔為你收集整理的golang 多协程的同步方法总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单计算机面试题库及答案_试讲可以看教案
- 下一篇: 哈希算法的用途