> For the complete documentation index, see [llms.txt](https://denglj.gitbook.io/essential-go/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://denglj.gitbook.io/essential-go/21-concurrency/wait-for-goroutines-to-finish.md).

# 等待协程结束

[Go程序随着`main()`函数的结束而结束](https://golang.org/ref/spec#Program_execution)，因此，通常的做法是等待所有协程执行完毕。

通常所用的解决方案是利用 [sync.WaitGroup](http://golang.org/pkg/sync/#WaitGroup) 对象。

```go
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup // 1

func routine(i int) {
	defer wg.Done() // 3
	fmt.Printf("routine %v finished\n", i)
}

func main() {
	wg.Add(10) // 2
	for i := 0; i < 10; i++ {
		go routine(i) // *
	}
	wg.Wait() // 4
	fmt.Println("main finished")
}

-----------Output-------------
routine 3 finished
routine 9 finished
routine 0 finished
routine 4 finished
routine 5 finished
routine 6 finished
routine 7 finished
routine 8 finished
routine 1 finished
routine 2 finished
main finished
```

WaitGroup 用法中的执行顺序：

1. 声明为全局变量。全局变量使得所有函数和方法都可以很简单地访问到它。
2. 增加计数器。这必须要在主协程中完成，因为内存模型的原因，并不保证[新启动的协程](https://golang.org/ref/mem#tmp_5)将在4之前执行完。
3. 减少计数器。这必须在每个协程退出的位置执行。通过使用延迟(`defer`)调用，我们可以确保[将在函数结束时调用](https://golang.org/ref/spec#Defer_statements)，不论函数是如何被结束的。
4. 等待计数器到达0。这也必须在主协程中完成，以避免主程序在所有协程尚未结束之前就退出了。

协程调用的参数[在其新启动之前](https://golang.org/ref/spec#Go_statements)就被求值。

因此，有必要在`wg.Add(10)`之前就显式地定义其值，以免遇到恐慌(`panic`)后，计数器无法正常增加。向`WaitGroup`中添加了10个条目，因此，将在`wg.Wait()` 把控制权交还`main()`协程之前等待10个条目。此例中，协程参数`i`的值是在`for`循环中定义的。
