最近在学golang,学到管道与协程,如何保证协程执行完毕之前主线程不退出呢?
可以在主线程最后加上time.Sleep(time.Second)等待协程执行完毕,但是我们没办法确定到底要等待多长时间,因此这种方法不合适。
方法一 利用管道
这是一个求素数的程序:
var intChan = make(chan int, 1000)
var primeChan = make(chan int, 2000)
var exitChan = make(chan bool, 4)
go putNum(intChan)
for i := 1; i <= 4; i++ {
go putPrime(intChan, primeChan, exitChan)
}
go func() {
for i := 0; i < 4; i++ {
<-exitChan
}
close(primeChan)
}()
for {
res, ok := <-primeChan
if !ok {
break
}
fmt.Println("素数有", res)
}
fmt.Println("主线程退出...")
利用四个协程求1-8000中的素数,每个协程结束时向exitChan写入一个true,再通过遍历exitChan,输出四个值后即可判断四个协程已经结束。
这么做的坏处是,开启n个协程就需要创建容量为n的管道,消耗内存。
方法二 利用sync.WaitGroup
sync.WaitGroup对象有三个方法
Add()
Done()
Wait()
其中,Add(n) 把计数器值设为n,一般要判断n个协程就设为n。在协程结束时利用Done()将计数器减一。Wait()会阻塞代码运行,直到计数器减为0。
var intChan = make(chan int, 1000)
var primeChan = make(chan int, 2000)
wg := sync.WaitGroup{}
wg.Add(4)
go putNum(intChan)
for i := 1; i <= 4; i++ {
go putPrime(intChan, primeChan, &wg)
}
wg.Wait()
close(primeChan)
for {
res, ok := <-primeChan
if !ok {
break
}
fmt.Println("素数有", res)
}
fmt.Println("主线程退出...")
由于WaitGroup不是引用类型,因此函数用到时必须使用指针。