等待所有的goroutine退出的两种方式
然在前面的文章中有略微记录过一点这部分的内容,但还是单独列出来讲一下吧。
让goroutine退出的方式,有以下两种:
1.使用channel来传递信号
2.使用waitGroup
方式一,如图golang社区的一句名言,不要通过共享内存来通信,而应该通过通信来共享内存
例子如下:
func run(done chan int ) {
for {
select {
case <-done :
fmt.Println("exiting...")
done <- -1
break
default:
fmt.Println("nothing...")
}
}
}
func main() {
ch := make(chan int)
go run(ch)
fmt.Println("wait")
time.Sleep(time.Second * 2)
ch <- 1
fmt.Println(<-ch)
fmt.Println("main exited")
}
复制代码
代码分析:在main函数中,创建了一个channel,并且开了一个goroutine,goroutine的逻辑是:无限循环,当从channel接受到一个值时,就打印离开的描述并且往channel发送一个数字-1表示可以结束了,否则一直等待。最后在main函数里打印接收到的值并且结束整个函数。
方式二,使用waitGroup退出goroutine,waitGroup有三个方法,Add(),Done(),Wait(),
在Golang源码中,Done实际上调用的是Add(-1)的方法,即在waitGroup中减去一个,
Golang中waitGroup中Done方法的源码
在使用waitGroup的时候,需要注意的是go文档的一句话:The main goroutine calls Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls Done when finished.
即在运行main函数的goroutine里运行Add()函数,在其他的goroutine里面运行Done()函数。
下面可以看一个购买的慕课网Go教程的一段代码:
type worker struct {
in chan int
done func()
}
func doWorker(i int, w worker) {
for n := range w.in {
fmt.Printf("Worker %d received %c\n", i, n)
w.done()
}
}
func createWorker(i int, wg *sync.WaitGroup) worker {
w := worker{
in: make(chan int),
done: func() {
wg.Done()
},
}
go doWorker(i, w)
return w
}
func chanDemo() {
var wg sync.WaitGroup
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i, &wg)
}
wg.Add(20)
for i, worker := range workers {
worker.in <- 'a' + i
fmt.Printf("print:%c \n",'a'+i )
}
for i, worker := range workers {
worker.in <- 'A' + i
fmt.Printf("print:%c\n",'A'+i )
}
wg.Wait()
}
func main() {
chanDemo()
}
复制代码
在main的主函数调用了waitGroup的Add的方法,告诉程序有20goroutine需要等待,然后调用wait执行阻塞,直到所有的WaitGroup数量变成0,然后在每一次goroutine操作结束后调用Done方法,来减少需要等待的goroutine的数量。
链接:https://juejin.im/post/5aa5286651882510fd3f3f68