在本文中,我们展示了如何使用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教程。
