最近在学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不是引用类型,因此函数用到时必须使用指针。