我们之前学习了协程和信道,里面有很多例子,当时为了保证main goroutine在所有的goroutine都执行完毕后在退出,我们使用了time.Sleep这种方式
由于写的demo都是很简单的,sleep个1秒,我们感觉应该是够用的
但是在实际开发中,我们无法预知,所有的goroutine需要多长时间才能执行完毕,sleep多了 主程序就会阻塞,sleep少了有的子协程的任务无法完成
我们今天来介绍一下 怎么优雅的处理这种情况
1.使用信道来标记完成
信道可以实现多个协程间的通信,那么我们只需要定义一个信道,在任务完成后,往信道中写入true,然后主协程中获取到true,就认为子协程执行完毕
import "fmt" func main() { done := make(chan bool) //开一个协程去执行 go func() { for i := 0; i < 5; i++ { fmt.Println(i) } //执行完毕,往信道里写入true done <- true }() //主协程中如果获取到信道里时true 就退出 <-done }
输出如下
0 1 2 3 4
2.使用WaitGroup
上面使用的方法,在单个协程或者协程数量比较少的时候不会有什么问题,但在协程数多的时候,代码就会很复杂
更优雅的方式就是使用WaitGroup
WaitGroup只要实例化了就能用
var 实例名 sync.WaitGroup
实例化完成后,就可以使用它的几个方法:
Add:初始值为0,你传入的值会往计数器上加,这里直接传入子协程的数量
Done: 当某个子协程完成后,可以调用此方法,就会从计数器上减1,通常可以使用defer来调用
Wait: 阻塞当前协程,知道计数器的值归0
举一个例子
// Startup the EventRouter //这里有一个协程, 所以计数器的值是1 wg.Add(1)
//协程 go func() { //defer的作用是在eventRouter.run函数执行完,在执行defer的语句,所以这里就是程序执行完了,执行wg.Done()把计数器里的值减1 defer wg.Done() eventRouter.Run(stop) }() // Startup the Informer(s) glog.Infof("Starting shared Informer(s)") sharedInformers.Start(stop) //主协程一直调用wg.Wait()方法阻塞着,直到计数器的值为0在执行后面的代码 wg.Wait()
//执行这个代码的时候,说明计数器归0了,wg.Wait失效,不会阻塞主协程了 glog.Warningf("Exiting main()") os.Exit(1)
上面使用信道的方法,在单个协程或者协程数少的时候,并不会有什么问题,但在协程数多的时候,代码就会显得非常复杂