父协程中生成子协程
-
问题:如果在一个父goroutine中生成了子goroutine,请问如果父goroutine先执行完毕,那么子协程会自动退出吗?
答案是:不会 -
先给出结论:
- 主协程执行完毕后,子协程会继续执行,直到它自己完成或程序退出。
- 主协程退出不会自动关闭子协程,它们是独立的执行单元。
- 如果需要确保所有协程都完成任务再退出,应该使用 sync.WaitGroup、context 或其他同步机制来确保协程之间的协作和终止。
main函数和普通函数的区别
- 实际上,main函数也和其他函数一样,只要内部开启了协程,那么这个协程将自己运行到结束为止。但是 main函数代表了一整个程序的入口,如果main函数执行完毕了,代表程序退出了,既然程序退出了,期间所有的资源将被销毁
- 具体而言:当一个程序正常退出(例如main函数返回)或者异常退出(例如遇到panic且没有被恢复)时,所有正在运行的goroutine都会被强制终止。这是因为整个程序的执行环境被销毁了。
- 普通函数: 即便return了,开启的协程依然执行下去
func GenerateIntA1() chan int {ch := make(chan int, 10)//启动一个goroutine用于生成随机数,函数返回一个通道用于获取随机数go func() {for {ch <- rand.Int()}}()return ch
}
如何看待这个特性?
好处很简单,就是在代码层面我们可以实现很多功能,例如上面的代码,返回一个通道之后可以不断从ch中获取数据,并且保证了全局唯一
- 坏处
- 使用不当容易导致协程泄漏,因为很多服务是一直开着不关闭,所以有些协程使用不当,会永远无法退出,资源一直在消耗
如何提高相关编程规范?
既然协程开了就不会随着父协程关闭,那就要注意开启的时候,考虑这个协程的生命周期何时终止。
具体而言注意点:
- 注意每个channel的close处理, 不然很容易出现异常, chan的读写很多时候是阻塞的,如果读大于写,或者写大于读,都可能导致一方远远阻塞
- 写数据goroutine中调用close,
- 不要多次调用close,
- 使用信号通知chan close了
- 多使用context,管理goroutine的生命周期