在本文中,我们展示了如何使用WaitGroup在Golang中等待goroutines完成。
$ go version go version go1.18.1 linux/amd64
我们使用Go版本1.18。
Goroutine是一个轻量级的执行线程。它是一个与其他正在运行的代码同时运行的函数。
WaitGroup
等待一组goroutine完成。它充当一个计数器,保存要等待的函数或goroutine的数量。WaitGroup
是同步包的一部分。
等待组函数
WaitGroup
具有三个函数:Add
、Done
和Wait
。
func (wg *WaitGroup) Add(delta int)
Add
函数将增量值添加到WaitGroup计数器。如果计数器变为零,则释放所有阻塞在Wait
上的goroutine。
func (wg *WaitGroup) Done()
Done
将WaitGroup
计数器减一。当goroutine完成执行时调用它。
func (wg *WaitGroup) Wait()
Wait
阻塞,直到WaitGroup
计数器为零。
运行协程
主程序本身就是一个协程。它可能比它调用的goroutines更早完成。
package main import ( "fmt" ) func f1() { fmt.Println("goroutine 1") } func f2() { fmt.Println("goroutine 2") } func main() { go f1() go f2() }
在程序中,我们在main函数中启动了两个goroutine。但是,程序在两个goroutine之前完成。
$ go run main.go
程序不打印任何输出。
package main import ( "fmt" "time" ) func f1() { fmt.Println("goroutine 1") } func f2() { fmt.Println("goroutine 2") } func main() { go f1() go f2() time.Sleep(2 * time.Second) }
当我们使用time.Sleep
休眠两秒钟时,goroutines有时间完成。
$ go run main.go goroutine 2 goroutine 1
GoWaitGroup简单例子
sync.WaitGroup
是一个等待goroutine集合完成的同步工具。
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup wg.Add(2) go func() { count("oranges") wg.Done() }() go func() { count("bananas") wg.Done() }() wg.Wait() } func count(thing string) { for i := 0; i < 4; i++ { fmt.Printf("counting %s\n", thing) time.Sleep(time.Millisecond * 500) } }
我们使用WaitGroup
同步两个goroutine的执行。
var wg sync.WaitGroup wg.Add(2)
通过Add
函数,我们可以告诉我们等待的goroutines有多少。
go func() { count("oranges") wg.Done() }()
我们创建一个匿名goroutine。我们用Done
告诉Go运行时goroutine已经完成。
wg.Wait()
Wait
函数会阻塞,直到所有goroutine完成。
time.Sleep(time.Millisecond * 500)
在演示程序中,time.Sleep
通常用于减慢goroutines的执行速度。
$ go run main.go counting bananas counting oranges counting oranges counting bananas counting bananas counting oranges counting oranges counting bananas
我们必须在函数中传递一个指向WaitGroup
的指针。
package main import ( "fmt" "sync" ) func f1(wg *sync.WaitGroup) { defer wg.Done() fmt.Println("goroutine 1") } func f2(wg *sync.WaitGroup) { defer wg.Done() fmt.Println("goroutine 2") } func main() { var wg sync.WaitGroup wg.Add(1) go f1(&wg) wg.Add(1) go f2(&wg) wg.Wait() }
在示例中,我们有两个goroutine。我们向它们传递一个指向WaitGroup
的指针。
GoWaitGroup异步HTTP请求
在下面的示例中,我们使用goroutines进行多个异步HTTP请求。
package main import ( "fmt" "log" "net/http" "sync" ) func main() { urls := []string{ "http://webcode.me", "https://example.com", "http://httpbin.org", "https://www.perl.org", "https://www.python.org", "https://clojure.org", } var wg sync.WaitGroup for _, u := range urls { wg.Add(1) go func(url string) { defer wg.Done() status := doReq(url) fmt.Printf("%s - %s\n", url, status) }(u) } wg.Wait() } func doReq(url string) (status string) { resp, err := http.Head(url) if err != nil { log.Println(err) return } return resp.Status }
我们发出多个异步HTTP请求。我们发出HEAD请求并返回响应的状态代码。每个请求都包装在一个goroutine中。
var wg sync.WaitGroup
WaitGroup
用于等待所有请求完成。
for _, u := range urls { wg.Add(1) go func(url string) { ... }(u) }
我们遍历url的一部分并向计数器添加一个goroutine。
go func(url string) { defer wg.Done() status := doReq(url) fmt.Printf("%s - %s\n", url, status) }(u)
使用goroutine,我们生成一个HEAD请求,接收响应,并打印状态代码。请求完成后,调用Done
函数来减少计数器。
$ go run main.go http://webcode.me - 200 OK https://www.python.org - 200 OK https://www.perl.org - 200 OK https://clojure.org - 200 OK http://httpbin.org - 200 OK https://example.com - 200 OK
在本文中,我们使用了WaitGroup
来等待goroutine的集合完成。
列出所有Go教程。