根据 go spec 中对于处理 panic 的描述: > 当执行一个函数 F
时,显式调用 panic
或者运行时的 panic 都会终止函数 F
的执行。此时,函数 F
中 defer 的函数会照常执行。接下来,执行函数 F
的调用者中 defer 的函数,依此执行至当前 goroutine 顶层 defer 的函数。执行完后,退出程序,并报告异常(包括 panic
函数的值)
换句话说,当 panic 出现时,会沿调用堆栈向上执行 defer 函数。这里要特别特别特别注意的是,只会执行当前 goroutine 的调用堆栈。也就是说,生成该 goroutine 的那个 goroutine 的 defer 函数是不会执行的!!!!
有点绕口,我们看个例子:
1 | package main |
上面的代码很简单。exception
函数首先 defer 一句退出日志,然后休眠 0.1s 后,抛出一个 panic。在主函数中,我们也 defer 一句退出日志,接着 defer 一个匿名函数,用于捕获 panic。然后,利用 exception
函数创建一个 goroutine,最后休眠 1s 等待 exception
函数执行完成。
上述代码的目的是,在主 goroutine 中能够处理另一个 goroutine 的 panic。然而,运行结果却与之相悖: 1
2
3
4
5
6
7
8
9test begins
exception exits
panic: throw something
goroutine 5 [running]:
main.exception()
/tmp/sandbox343049822/main.go:12 +0x180
created by main.main
/tmp/sandbox343049822/main.go:23 +0x100exception
执行到 panic
处时,并没有继续执行下去,而是开始执行 defer 的函数。接着,抵达当前 goroutine (id=5)的顶部后,退出程序,并抛出异常信息。此时,main goroutine 中 defer 的函数并没有执行。
一句话总结:只能在当前 goroutine 处理 panic,不能在当前 goroutine 处理其他 goroutine 产生的 panic!