异常处理与协程
在讨论这个问题之前我们应该明确:Go不支持跨协程处理异常。
下面这个例子中,虽然在main中处理了异常但是异常并没有被捕获,所以这说明当前协程并不能处理其它协程中的panic,其实在Go异常处理(三)中我们已经提及了,gopanic函数中如果没有调用mcall退出函数,则在最后会调用fatalpanic函数从而间接触发exit函数而导致函数退出,这也就是为什么协程异常会导致整个程序崩溃。
package main
import (
"log"
"time"
)
func main() {
defer func() {
if err := recover(); err != nil {
// 这段代码并不会被调用
log.Println("main:",err)
}
}()
// 在协程中抛出异常
go func() {
panic("error")
}()
time.Sleep(time.Second*10)
}
为什么不支持跨协程处理异常
在开始讨论之前,在此声明:以下所有的内容目前都是猜测,还没有验证这个猜想是否完全正确,这目前只是一个研究方向。
- 猜测1:g对象并没有记录当前g关联的父g是哪个,所以没有办法将错误信息传递过去。
- 猜测2:协程的执行是在不同的线程中执行,而且协程的执行并没有先后顺序,这也导致了无法确定抛出panic的协程是不是在父协程前执行完成的。
- 猜测3:如果多个协程中都抛出了异常,但是协程的执行顺序又不固定,那Go怎么知道哪个异常应该是第一个被处理的呢?
- 猜测4:当然如果g中保存了协程间的引用关系,自然知道异常的抛出顺序是什么的,那么这样做是不是会影响go的性能呢?
- 猜测5:如果Go将所有的异常信息保存到一个全局的异常对象中,会不会因为频繁的lock导致性能降低呢?