WaitGroup
最后更新于:2022-04-02 06:50:35
如果你不关心并发操作的结果,或者有其他方式收集结果,那么WaitGroup是等待一组并发操作完成的好方法。如果这两个条件都不成立,我建议你改用channel和select语句。WaitGroup非常有用,我先介绍它,以便在后续章节中使用它。以下是使用WaitGroup等待goroutine完成的基本示例:
```
var wg sync.WaitGroup
wg.Add(1) //1
go func() {
defer wg.Done() //2
fmt.Println("1st goroutine sleeping...")
time.Sleep(1)
}()
wg.Add(1) //1
go func() {
defer wg.Done() //2
fmt.Println("2nd goroutine sleeping...")
time.Sleep(2)
}()
wg.Wait() //3
fmt.Println("All goroutines complete.")
```
1. 这里我们调用Add并传入参数1来表示一个goroutine正在开始。
2. 在这里我们使用defer关键字来调用Done,以确保在退出goroutine的闭包之前,向WaitGroup表明了我们已经退出。
3. 在这里,我们调用Wait,这将main goroutine,直到所有的goroutine都表明它们已经退出。
这会输出:
```
2nd goroutine sleeping...
1st goroutine sleeping...
All goroutines complete.
```
你可以把WaitGroup视作一个安全的并发计数器:调用Add增加计数,调用Done减少计数。调用Wait会阻塞并等待至计数器归零。
请注意,Add的调用是在goroutines之外完成的。 如果没有这样做,我们会引入一个数据竞争条件,因为我们没有对goroutine做任何调度顺序上的保证; 我们可能在任何一个goroutines开始前触发Wait调用。 如果Add的调用被放置在goroutines的闭包中,对Wait的调用可能完全没有阻塞地返回,因为Add没有被执行。
通常情况下,尽可能与要跟踪的goroutine就近且成对的调用Add,但有时候会一次性调用Add来跟踪一组goroutine。我通常会做这样的循环:
```
hello := func(wg *sync.WaitGroup, id int) {
defer wg.Done()
fmt.Printf("Hello from %v!\n", id)
}
const numGreeters = 5
var wg sync.WaitGroup
wg.Add(numGreeters)
for i := 0; i < numGreeters; i++ {
go hello(&wg, i+1)
}
wg.Wait()
```
这会输出:
```
Hello from 5!
Hello from 4!
Hello from 3!
Hello from 2!
Hello from 1!
```
* * * * *
学识浅薄,错误在所难免。我是长风,欢迎来Golang中国的群(211938256)就本书提出修改意见。
';