sync.WaitGroup可以使得我们很优雅的等待协程的退出

写一个普通的例子。下面的例子中,say函数中的字符串不会输出,因为main函数也是一个协程,say函数相当于一个子协程,父协程运行完退出后,子协程也会退出,并不会等待子协程中的任务完成,而要想执行完子协程中的任务,最简单的方法是让父协程等待一段时间,子协程可以在这段时间内执行完任务。去掉下面的注释后,say函数就可以执行完内部的任务。

package main

import (
	"fmt"
	"time"
)

func say() {
	fmt.Println("hello world")
}

func main() {
	for i := 0; i < 10; i++ {
		go say()
	}
	fmt.Println("I Love You")
	// time.Sleep(time.Second)  // 去掉注释
}

但是,我们并不知道子协程要执行多长时间,当执行I/O操作时,我们无法确保准确的时间,所以以上的方法不能用于正常的逻辑中。

(wg * WaitGroup) Add(delta int)(wg *WaitGroup) Done()(wg *WaitGroup) Wait()
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup  // 定义全局的结构体变量

func say() {
	defer wg.Done()  // 计数器 -1
	fmt.Println("hello world")
}

func main() {
	wg.Add(5)  // 计数器为5
	for i := 0; i < 5; i++ {
		go say()
	}
	fmt.Println("I Love You")
	wg.Wait()  // 阻塞父协程
	fmt.Println("I Love You")
}
/* 结果
I Love You
hello world
hello world
hello world
hello world
hello world
I Love You
*/