您好,登錄后才能下訂單哦!
這篇文章主要介紹了如何解決Golang中使用WaitGroup的問題,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
golang是一種編譯語言,可以將代碼編譯為機器代碼,編譯后的二進制文件可以直接部署到目標機器而無需額外的依賴,所以golang的性能優于其他的解釋性語言,且可以在golang中使用goroutine來實現并發性,它提供了一個非常優雅的goroutine調度程序系統,可以很容易地生成數百萬個goroutine。
sync.WaitGroup對于Golang開發者來說并不陌生,其經常作為多協程之間同步的一種機制。用好它勢必會讓你事半功倍,但是一旦錯用將引發問題。
關于WaitGroup的使用網上有很多例子,在此就不做介紹了,我想說的是我在項目中使用WaitGroup遇到的坑。
在項目中,因為服務器有同步需求, 所以直接使用了WaitGroup,但是未考慮使用場景,結果在項目上線之后,高峰期的時候客戶端經常出現卡頓,經過多方查找,才發現如果使用WaitGroup的時候,未啟動單獨的goroutine,那么極有可能造成主線程的阻塞
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
將WaitGroup用于goroutine內,不會導致主線程的阻塞,同樣可以實現同步的效果。
補充:WaitGroup的簡單用法(等待組)
你品一下人家這名字,等待組。等待什么,等待goroutine完成啊。有些時候,我們啟動多個goroutine去執行任務,我舉個例子
listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"} for _, ip := range(listip) { //假設我們執行一個ping ip 的邏輯 go PingIPWork(ip) }
我這里執行了一個多ip去ping的邏輯,一般這種時候,你要是執行一波,人家肯定毛都不會返回給你,為什么?因為人家主線程直接就退出了,還是那句話,你又沒告訴人家主線程要等這ip全部都ping 完,所以你必須要加個等待,等著Goroutine完成,這里我再舉一個網上的例子
package main import ( "fmt" ) func main() { go func() { fmt.Println("Goroutine 1") }() go func() { fmt.Println("Goroutine 2") }() //來個睡眠,等Goroutine結束 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) { //假設我們執行一個ping ip 的邏輯 go PingIPWork(ip) } time.Sleep(time.Second * 1)
加個sleep可以等待完成,但是萬一啊,Goroutine有的跑的快,有的慢,你那sleep就一秒,要是有的Goroutine沒跑完不就白瞎了嗎,所以咱們需要一個機制,這個機制可以幫助咱們去管理Goroutine,讓我們知道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,咱們定義三個動作,所以來三個計數 listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"} for _, ip := range(listip) { //假設我們執行一個ping ip 的邏輯 go func(ip string) { //執行一個work PingIPWork(ip) //操作完成之后,done一個計數,也就是3-1 wg.Done() }(ip) } //等待 wg.Wait() // 等待,直到計數為0 }
這里我舉了一個簡單的例子,其實wg的用法較為簡單,在這個例子里面我們用到了
wg.wait 等待Goroutine結束之后退出主進程 wg.Add 添加Goroutine,其實你可以把它想成,可添加的最大Goroutine數 wg.Done 想象成銷毀參數,當Goroutine結束之后調用,意思就是,你沒了,我減1
將Wg作為參數進行傳遞的時候,需要使用指針
有些時候,咱們不想寫的這么麻煩,就尋思怎么才能簡單一點,或者可變性稍微強一點,有些時候我們要把wg最為參數,在函數內部調用,我們該怎么寫呢?
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個計數 listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"} for _, ip := range(listip) { //假設我們執行一個ping ip 的邏輯 go PingIPWork(ip, &wg) } wg.Wait() // 等待,直到計數為0 }
看到了么,如果你把Wg作為參數進行傳遞,你得要用指針的形式傳值,否則就會死鎖!!!!!!!!
Wg.Add的數值不能為負
wg.Add()的數值必須為正數,如果為負數,將會拋出異常。
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
感謝你能夠認真閱讀完這篇文章,希望小編分享的“如何解決Golang中使用WaitGroup的問題”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。