您好,登錄后才能下訂單哦!
怎么在golang中利用Sync.WaitGroup解決等待問題?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
golang可以做服務器端開發,但golang很適合做日志處理、數據打包、虛擬機處理、數據庫代理等工作。在網絡編程方面,它還廣泛應用于web應用、API應用等領域。
面對goroutine我們都需要等待它完成交給它的事情,等待它計算完成或是執行完畢,所以不得不在程序需要等待的地方使用time.Sleep()來睡眠一段時間,等待其他goroytine執行完畢,下面的代碼打印1到100的for循環可以在很快的時間內運行完畢,但是我們必須添加time.Sleep()來等待其打印完畢,如果我們不等待仿佛什么也沒有發生一樣。。。。。這肯定不是我們想要的!
func main(){ for i := 0; i < 100 ; i++{ go fmt.Println(i) } time.Sleep(time.Second) }
這就是我最開始學習goroutine想到的笨方法。。。。死等! 等不到 time.Second*2 在等不到*3
但是對于我們實際的使用場景來說,1秒也許等待的時間有點長,而有時候是遠遠不夠的,最主要的我們無法預知gorotine的運行時間長短,所以也無法指定Sleep睡眠的時間。
正好前幾天學習了channel,那么現在我們就可以
func main() { c := make(chan bool, 100) for i := 0; i < 100; i++ { go func(i int) { fmt.Println(i) c <- true }(i) } for i := 0; i < 100; i++ { <-c } }
完美輸出!這里我們可以從控制臺看到我們的勝利成果,但是我們有更好的處理方式:sync.WaitGroup
WaitGroup對象內部有一個計數器,最初從0開始,它有三個方法:Add() Done() Wait() 用來控制計數器的數量從而達到等待的目的。
func main() { wg := sync.WaitGroup{} wg.Add(100) for i := 0; i < 100; i++ { go func(i int) { defer wg.Done() fmt.Print(i) }(i) } wg.Wait() }
首先使用 add(100) 把計數器的數量設置為100,每次循環運行完畢都把計數器減1,主函數使用Wait()
阻塞,直到計數器歸0結束主線程結束阻塞。
這里會阻塞主線程!!!需要注意。
type WaitGroup struct { noCopy noCopy state1 [3]uint32 }
它只是一個struct所以當參數傳遞的時候需要傳遞指針。
補充:sync.WaitGroup的錯誤用法與糾正
本文主要是記錄在學習golang語言過程中錯誤使用sync.WaitGroup以及指針引起的報錯,并且糾正過程。
package main import ( "fmt" "pressure/game" "sync" ) var waitGroup sync.WaitGroup //定義一個同步等待的組 func main(){ waitGroup.Add(1) //添加一個計數 go game.ConnSocket(serverAddr, waitGroup) //調用其他包的方法執行任務 waitGroup.Wait() //阻塞直到所有任務完成 fmt.Println("main DONE!!!") }
package game import ( "fmt" "net" "strconv" "strings" "sync" ) var gameWait sync.WaitGroup func ConnSocket(serverAddr string, wait sync.WaitGroup) { var err error Conn, err = net.Dial("tcp", serverAddr) if err != nil { fmt.Println("Error connected:", err.Error()) wait.Done() return } gameWait = wait fmt.Println("connected OK:", Conn.RemoteAddr()) go readMessage() } func readMessage() { fmt.Println("readMessage:") for { if OK:=dosomething(),OK{ gameWait.Done() } } }
然后執行如上的代碼,會出現報錯:
panic: sync: negative WaitGroup counter
goroutine 12 [running]:
sync.(*WaitGroup).Add(0x7b8b90, 0xffffffffffffffff)
D:/Program Files/Go/src/sync/waitgroup.go:75 +0x1d0
sync.(*WaitGroup).Done(0x7b8b90)
原因:
func ConnSocket(serverAddr string, wait sync.WaitGroup)只是把sync.WaitGroup的值傳遞過來,而非內存地址,所以在賦值給gameWait的時候也只是賦值了值,gameWait并非指向的main里的waitGroup 的內存地址,導致 gameWait.Done()的時候并沒有獲取到main里的計數1,所以才會報負數。究其原因還是沒有對指針的理解。所以針對上面的問題做了修改。
package main import ( "fmt" "pressure/game" "sync" ) var waitGroup sync.WaitGroup //定義一個同步等待的組 func main(){ waitGroup.Add(1) //添加一個計數 //這里傳遞waitGroup的內存地址 go game.ConnSocket(serverAddr, &waitGroup) //調用其他包的方法執行任務 waitGroup.Wait() //阻塞直到所有任務完成 fmt.Println("main DONE!!!") }
package game import ( "fmt" "net" "strconv" "strings" "sync" ) var gameWait *sync.WaitGroup//此處也申明為指針變量 //參數傳遞為指針傳遞*sync.WaitGroup,即wait 指向的是main里的waitGroup的內存地址 func ConnSocket(serverAddr string, wait *sync.WaitGroup) { var err error Conn, err = net.Dial("tcp", serverAddr) if err != nil { fmt.Println("Error connected:", err.Error()) wait.Done() return } gameWait = wait//指針指向另一個指針,這樣就做到了gameWait指向的是wait的地址,所以gameWait修改wait的內存地址鎖存貯的值也會跟隨改變 fmt.Println("connected OK:", Conn.RemoteAddr()) go readMessage() } func readMessage() { fmt.Println("readMessage:") for { if OK:=dosomething(),OK{ gameWait.Done() } } }
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。