golang 多協程的同步方法總結
之前用 go 寫一個小工具的時候, 用到了多個協程之間的通訊, 當時隨手查了查, 結果查出來一大坨, 簡單記錄一下. golang
中多個協程之間是如何進行通訊及資料同步的嘞.
共享變數
一個最簡單, 最容易想到的, 就是通過全域性變數的方式, 多個協程讀寫同一個變數. 但對同一個變數的更改, 就不得不加鎖了, 否則極易引發資料問題. 一般系統庫都提供基本的鎖, go 也提供了.
package main
import (
"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 + 1
mutex.Unlock()
}
僅執行一次
當查詢鎖查到sync
這個模組時, 發現它下面的物件並沒有幾個, 都是針對協程同步的各個方面給出的解決方案. 所以我就一個一個看文件試了試.
當你需要對環境, 連線池等等資源進行初始化時, 這種操作只需要執行一次, 這時候就需要它了. sync.Once
物件可以保證僅執行一次. 和 init 方法有些類似, 不過 init 方法是在模組首次載入時執行, 而sync.Once
是在首次呼叫時執行. (其實現就是一個計數器加一個互斥鎖)
package main
import (
"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 main
import (
"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 main
import (
"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 main
import (
"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)
// 遍歷 map
m.Range(func(key, value interface{}) bool {
// 返回 false 結束遍歷
return true
})
// 讀取變數, 若不存在則設定
m.LoadOrStore("1", 3)
// 刪除 key
m.Delete("1")
// 讀取變數
load, _ := m.Load("1")
fmt.Println(load)
}
多協程物件池
對於資料庫連線池應該並不陌生. 而sync.Pool
物件是go
封裝的協程安全的物件池. 物件池的使用十分簡單, 存/取
package main
import (
"sync"
)
var p = sync.Pool{
// 當池子中沒有物件了, 用於建立新物件
New: func() interface {}{
return "3"
},
}
func main() {
// 從池子中獲取一個物件
r := p.Get()
// 用完後將物件放回池子中
p.Put(r)
}
sync 簡單總結
針對go
系統的sync
模組, 提供的基礎功能如下:
- 互斥鎖
Mutex
- 讀寫鎖
RWMutex
- 函式單次執行
Once
- 協程執行等待
WaitGroup
- 協程訊息通知
Cond
- 多協程 map
Map
- 多協程物件池
Pool
幾個都簡單試過之後, 發現sync
模組針對常用的幾個多協程工具進行了封裝, 想來可以基本滿足日常使用了.
終極通訊-channel
channel
是一個協程安全的通訊管道, 簡單理解為資料從一側放入, 從另一側拿出. 這玩意感覺能玩出花來, 還不太理解, 留到國慶研究.
相關文章
- golang: 用協程非同步寫日誌Golang非同步
- Golang 協程Golang
- 使goroutine同步的方法總結Go
- python 協程用法總結(一)Python
- 非同步與協程非同步
- golang協程池設計Golang
- golang學習筆記(二)—— 深入golang中的協程Golang筆記
- 基於多執行緒+協程的非同步增量式爬蟲執行緒非同步爬蟲
- 同步協程的必備工具: WaitGroupAI
- Go 多協程記錄執行結果Go
- Golang協程併發的流水線模型Golang模型
- Golang協程池(workpool)實現Golang
- Golang —— goroutine(協程)和channel(管道)Golang
- golang omitempty 總結GolangMIT
- Vue 中 Promise 的then方法非同步使用及async/await 非同步使用總結VuePromise非同步AI
- Golang 的 協程排程機制 與 GOMAXPROCS 效能調優Golang
- 去除csdn廣告的方法,多種方法比較總結
- Golang非CSP併發模型外的其他並行方法總結Golang模型並行
- golang 面試總結Golang面試
- golang知識總結Golang
- 非同步爬蟲之理解協程非同步爬蟲
- 爬蟲之多工非同步協程爬蟲非同步
- Python非同步協程(asyncio詳解)Python非同步
- 一日一技:協程與多程式的完美結合
- 關於golang的time包總結Golang
- 多型的總結多型
- 對執行緒、協程和同步非同步、阻塞非阻塞的理解執行緒非同步
- 線上MYSQL同步報錯故障處理方法總結MySql
- php 非同步上傳圖片幾種方法總結PHP非同步
- golang 常見疑惑總結Golang
- 【python】非同步IO | 協程 | asyncio | await | yieldPython非同步AI
- GoLang協程Goroutiney原理與GMP模型詳解Golang模型
- PHP 多工協程處理PHP
- 多協程執行後使用channel收集結果--優雅版本
- 多協程執行後使用channel收集結果--初級版本
- Golang協程無法固定goroutine的最大數目解決Golang
- LocalDateTime的方法總結LDA
- 等待多個非同步任務的方法非同步