解決Golang 中使用WaitGroup的那點坑
sync.WaitGroup對于Golang開發(fā)者來說并不陌生,其經(jīng)常作為多協(xié)程之間同步的一種機制。用好它勢必會讓你事半功倍,但是一旦錯用將引發(fā)問題。
關(guān)于WaitGroup的使用網(wǎng)上有很多例子,在此就不做介紹了,我想說的是我在項目中使用WaitGroup遇到的坑。
在項目中,因為服務(wù)器有同步需求, 所以直接使用了WaitGroup,但是未考慮使用場景,結(jié)果在項目上線之后,高峰期的時候客戶端經(jīng)常出現(xiàn)卡頓,經(jīng)過多方查找,才發(fā)現(xiàn)如果使用WaitGroup的時候,未啟動單獨的goroutine,那么極有可能造成主線程的阻塞
所以我做了下面的測試(測試中,我把WaitGroup置于協(xié)程內(nèi)):
import (
"fmt"
"sync"
"time"
)
func main() {
fmt.Println("main-1")
testW()
fmt.Println("main-2")
time.Sleep(time.Duration(15) * time.Second)
}
func testW() {
fmt.Println("testW-1")
go func() {
var wg sync.WaitGroup
fmt.Println("testW-2")
testW1(&wg)
fmt.Println("testW-5")
wg.Wait()
fmt.Println("testW-6")
}()
}
func testW1(wg *sync.WaitGroup) {
wg.Add(1)
fmt.Println("testW-3")
time.AfterFunc(time.Second*5, func() {
wg.Done()
})
fmt.Println("testW-4")
}
輸出為:
main-1
testchan-1
main-2
testchan-2
testchan-3
testchan-4
testchan-5
// 過5秒
testchan-6
總結(jié):
將WaitGroup用于goroutine內(nèi),不會導(dǎo)致主線程的阻塞,同樣可以實現(xiàn)同步的效果。
補充:WaitGroup的簡單用法(等待組)
你品一下人家這名字,等待組。等待什么,等待goroutine完成啊。有些時候,我們啟動多個goroutine去執(zhí)行任務(wù),我舉個例子
listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"}
for _, ip := range(listip) {
//假設(shè)我們執(zhí)行一個ping ip 的邏輯
go PingIPWork(ip)
}
我這里執(zhí)行了一個多ip去ping的邏輯,一般這種時候,你要是執(zhí)行一波,人家肯定毛都不會返回給你,為什么?因為人家主線程直接就退出了,還是那句話,你又沒告訴人家主線程要等這ip全部都ping 完,所以你必須要加個等待,等著Goroutine完成,這里我再舉一個網(wǎng)上的例子
package main
import (
"fmt"
)
func main() {
go func() {
fmt.Println("Goroutine 1")
}()
go func() {
fmt.Println("Goroutine 2")
}()
//來個睡眠,等Goroutine結(jié)束
time.Sleep(time.Second * 1)
}
看到了么,加了一個sleep,用sleep去等著Goroutine跑完,上面我舉的那個例子也可以這么來
listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"}
for _, ip := range(listip) {
//假設(shè)我們執(zhí)行一個ping ip 的邏輯
go PingIPWork(ip)
}
time.Sleep(time.Second * 1)
加個sleep可以等待完成,但是萬一啊,Goroutine有的跑的快,有的慢,你那sleep就一秒,要是有的Goroutine沒跑完不就白瞎了嗎,所以咱們需要一個機制,這個機制可以幫助咱們?nèi)ス芾鞧oroutine,讓我們知道Goroutine這東西什么時候停,什么時候完成。
所以,WaitGroup這個東西,就可以幫助我們解決這個問題,還是老樣子,我舉一個簡單的例子來說明我的想法。
package main
import (
"fmt"
"sync"
)
func PingIPWork(ip string) {
fmt.Println(ip)
}
func main() {
//定義一個等待阿祖
var wg sync.WaitGroup
wg.Add(3) // 因為有3個Ip,咱們定義三個動作,所以來三個計數(shù)
listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"}
for _, ip := range(listip) {
//假設(shè)我們執(zhí)行一個ping ip 的邏輯
go func(ip string) {
//執(zhí)行一個work
PingIPWork(ip)
//操作完成之后,done一個計數(shù),也就是3-1
wg.Done()
}(ip)
}
//等待
wg.Wait() // 等待,直到計數(shù)為0
}
這里我舉了一個簡單的例子,其實wg的用法較為簡單,在這個例子里面我們用到了
wg.wait 等待Goroutine結(jié)束之后退出主進程 wg.Add 添加Goroutine,其實你可以把它想成,可添加的最大Goroutine數(shù) wg.Done 想象成銷毀參數(shù),當(dāng)Goroutine結(jié)束之后調(diào)用,意思就是,你沒了,我減1
WaitGroup的其他注意事項
將Wg作為參數(shù)進行傳遞的時候,需要使用指針
有些時候,咱們不想寫的這么麻煩,就尋思怎么才能簡單一點,或者可變性稍微強一點,有些時候我們要把wg最為參數(shù),在函數(shù)內(nèi)部調(diào)用,我們該怎么寫呢?
package main
import (
"fmt"
"sync"
)
func PingIPWork(ip string, wg *sync.WaitGroup) {
fmt.Println(ip)
wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(3) // 因為有兩個動作,所以增加2個計數(shù)
listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"}
for _, ip := range(listip) {
//假設(shè)我們執(zhí)行一個ping ip 的邏輯
go PingIPWork(ip, &wg)
}
wg.Wait() // 等待,直到計數(shù)為0
}
看到了么,如果你把Wg作為參數(shù)進行傳遞,你得要用指針的形式傳值,否則就會死鎖?。。。。。。?!
Wg.Add的數(shù)值不能為負(fù)
wg.Add()的數(shù)值必須為正數(shù),如果為負(fù)數(shù),將會拋出異常。
panic: sync: negative WaitGroup counter
goroutine 1 [running]:
sync.(*WaitGroup).Add(0xc042008230, 0xffffffffffffff9c)
D:/Go/src/sync/waitgroup.go:75 +0x1d0
main.main()
D:/code/go/src/test-src/2-Package/sync/waitgroup/main.go:10 +0x54
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
- Go語言WaitGroup使用時需要注意的坑
- Golang中的sync.WaitGroup用法實例
- Go并發(fā):使用sync.WaitGroup實現(xiàn)協(xié)程同步方式
- 在golang中使用Sync.WaitGroup解決等待的問題
- Go語言學(xué)習(xí)之WaitGroup用法詳解
- golang?waitgroup的具體使用
- Golang 標(biāo)準(zhǔn)庫 tips之waitgroup詳解
- Go語言并發(fā)之WaitGroup的用法詳解
- Go并發(fā)控制WaitGroup的使用場景分析
- 解析Go的Waitgroup和鎖的問題
- Go并發(fā)原子操作 waitGroup 對象池
相關(guān)文章
Golang執(zhí)行g(shù)o get私有庫提示"410 Gone" 的問題及解決辦法
這篇文章主要介紹了Golang執(zhí)行g(shù)o get私有庫提示”410 Gone“ 解決辦法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02
go語言實現(xiàn)簡單的并發(fā)網(wǎng)頁爬蟲示例
在這個章節(jié)中,我們將會結(jié)合之前的知識點,實現(xiàn)一個簡易的并發(fā)網(wǎng)頁爬蟲,我們的爬蟲會先爬取一個起始頁面,提取出所有的鏈接,然后并發(fā)地爬取這些鏈接,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-08-08
Golang教程之不可重入函數(shù)的實現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Golang教程之不可重入函數(shù)的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09
深入解析Go語言編程中slice切片結(jié)構(gòu)
這篇文章主要介紹了Go語言編程中slice切片結(jié)構(gòu),其中Append方法的用法介紹較為詳細(xì),需要的朋友可以參考下2015-10-10

