等待协程结束

Go程序随着main()函数的结束而结束,因此,通常的做法是等待所有协程执行完毕。

通常所用的解决方案是利用 sync.WaitGroup 对象。

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. 增加计数器。这必须要在主协程中完成,因为内存模型的原因,并不保证新启动的协程将在4之前执行完。

  3. 减少计数器。这必须在每个协程退出的位置执行。通过使用延迟(defer)调用,我们可以确保将在函数结束时调用,不论函数是如何被结束的。

  4. 等待计数器到达0。这也必须在主协程中完成,以避免主程序在所有协程尚未结束之前就退出了。

协程调用的参数在其新启动之前就被求值。

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

最后更新于