Go語言并發(fā)之WaitGroup的用法詳解
1、Go語言并發(fā)之WaitGroup
goroutine 和 chan,一個用于并發(fā),另一個用于通信。沒有緩沖的通道具有同步的功能,除此之外,sync 包也提
供了多個 goroutine 同步的機制,主要是通過 WaitGroup 實現(xiàn)的。
WaitGroup 用來等待多個 goroutine 完成,main goroutine 調用 Add 設置需要等待 goroutine 的數(shù)目,每一個
goroutine 結束時調用 Done(),Wait() 被 main 用來等待所有的 goroutine 完成。
主要數(shù)據(jù)結構和操作如下:
type WaitGroup struct { // contains filtered or unexported fields } // 添加等待信號 func (wg*WaitGroup) Add (delta int) // 釋放等待信號 func (wg*WaitGroup) Done() // 等待 func (wg*WaitGroup) Wait()
下面的程序演示如何使用 sync.WaitGroup 完成多個 goroutine 之間的協(xié)同工作。
package main import ( "net/http" "sync" ) var wg sync.WaitGroup var urls = []string{ "https://news.sina.com.cn/", "https://www.bilibili.com/", "https://www.qq.com/", } func main() { for _, url := range urls { //每一個url啟動一個goroutine,同時給wg加1 wg.Add(1) // 啟動一個goroutine獲取URL go func(url string) { // 當前goroutine結束后給wg計數(shù)減1 ,wg.Done()等價于wg.Add(-1) // defer wg.Add(-1) defer wg.Done() // 發(fā)送http get請求并打印http返回碼 resp, err := http.Get(url) if err == nil { println(resp.Status) } }(url) } // 等待所有HTTP獲取完成 wg.Wait() }
# 輸出
501 Not Implemented
200 OK
200 OK
1.1 不加鎖
多線程中使用睡眠函數(shù)不優(yōu)雅,直接用 sync.WaitGroup 保證一個 goroutine 剛退出就可以繼續(xù)執(zhí)行,不需要自
己猜需要 sleep 多久。
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func main() { // 啟動一個goroutine就登記+1,啟動十個就+10 wg.Add(10) var count = 0 for i := 0; i < 10; i++ { go func() { // goroutine結束就登記-1 defer wg.Done() for j := 0; j < 100000; j++ { count++ } }() } // 等待所有登記的goroutine都結束 wg.Wait() // 346730 fmt.Print(count) }
啟動十個goroutine對count自增10w次,理想狀況為100w,但由于沒有鎖,會出現(xiàn)實際情況遠遠小于并且不相等
的情況。為什么會出現(xiàn)這樣的結果?因為自增并不是一個原子操作,很可能幾個goroutine同時讀到同一個數(shù),自
增,又將同樣的數(shù)寫了回去。
1.2 互斥鎖
1.2.1 直接使用鎖
共享資源是count變量,臨界區(qū)是count++,臨界區(qū)之前加鎖,使其他goroutine在臨界區(qū)阻塞,離開臨界區(qū)解
鎖,就可以解決這個 data race 的問題。go語言是通過 sync.Mutex 實現(xiàn)這一功能。
package main import ( "fmt" "sync" ) var wg sync.WaitGroup func main() { // 定義鎖 var mu sync.Mutex wg.Add(10) var count = 0 for i := 0; i < 10; i++ { go func() { defer wg.Done() for j := 0; j < 100000; j++ { // 加鎖 mu.Lock() count++ // 解鎖 mu.Unlock() } }() } wg.Wait() // 1000000 fmt.Print(count) }
1.2.2 嵌入字段方式使用鎖
把 Mutex 嵌入 struct,可以直接在這個 struct 上使用 Lock/Unlock。
package main import ( "fmt" "sync" ) var wg sync.WaitGroup type Counter struct { sync.Mutex Count uint64 } func main() { var counter Counter wg.Add(10) for i := 0; i < 10; i++ { go func() { defer wg.Done() for j := 0; j < 100000; j++ { counter.Lock() counter.Count++ counter.Unlock() } }() } wg.Wait() // 1000000 fmt.Print(counter.Count) }
1.2.3 把加/解鎖封裝成方法
package main import ( "fmt" "sync" ) var wg sync.WaitGroup type Counter struct { mu sync.Mutex count uint64 } func (c *Counter) Incr() { c.mu.Lock() c.count++ c.mu.Unlock() } func (c *Counter) Count() uint64 { c.mu.Lock() defer c.mu.Unlock() return c.count } func main() { var counter Counter // 啟動一個goroutine就登記+1,啟動十個就+10 wg.Add(10) for i := 0; i < 10; i++ { go func() { // goroutine結束就登記-1 defer wg.Done() for j := 0; j < 100000; j++ { counter.Incr() } }() } // 等待所有登記的goroutine都結束 wg.Wait() // 1000000 fmt.Print(counter.Count()) }
到此這篇關于Go語言并發(fā)之WaitGroup的用法詳解的文章就介紹到這了,更多相關Go語言 WaitGroup內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
golang給函數(shù)參數(shù)設置默認值的幾種方式小結(函數(shù)參數(shù)默認值
在日常開發(fā)中我們有時候需要使用默認設置,下面這篇文章主要給大家介紹了關于golang給函數(shù)參數(shù)設置默認值的幾種方式小結的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-01-01