关于 defer
平时没注意,今天偶然发现子协程 panic 后,主协程的 defer 内的操作不会执行?要说捕捉不到子协程的异常可以理解,请问下有相关的文档说明这个现象的吗?
func Test_X(t *testing.T) { defer log.Println(111) go func() { panic(222) }() }
Out
=== RUN Test_X panic: 222
平时没注意,今天偶然发现子协程 panic 后,主协程的 defer 内的操作不会执行?要说捕捉不到子协程的异常可以理解,请问下有相关的文档说明这个现象的吗?
func Test_X(t *testing.T) { defer log.Println(111) go func() { panic(222) }() }
Out
=== RUN Test_X panic: 222
要解决这个问题,使用 waitGroup 或者 channel & …
note:这不是一个 bug,而是协程以及异步本身的特性
A “defer” statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.
文档指明了在“the corresponding goroutine” panic 的时候才会 defer 。
为了确定 panic 在一个 goroutine 中的行为,文档也描述了,在 https://golang.org/ref/spec#Handling_panics,我把它贴过来
While executing a function F, an explicit call to panic or a run-time panic terminates the execution of F. Any functions deferred by F are then executed as usual. Next, any deferred functions run by F’s caller are run, and so on up to any deferred by the top-level function in the executing goroutine. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking.
注意到”in the executing goroutine”的限制,那么就是说 panic 会触发在它所运行的 goroutine 中调用链上的 defer,然后整个程序就退出了。所以 panic 不会管其他 goroutine 中的 defer 。
抱歉对你的问题有曲解,如 8#所言,defer 的执行条件是有 return,所以你需要 recover 以保障主协程中 return 的执行
main 协程和子协程执行顺序未知,都有可能先执行结束
main 先执行完:会执行 main 协程中的 defer
子协程先执行完:panic 信息
输出结果有几种可能:
1. main 协程先于子协程结束
输出:111
2. 子协程先 panic,main 协程未执行到 println(111)
输出:panic 222 。。。
3. 子协程先 panic,main 协程执行到 println(111)
输出:
111
panic 222 。。。
2/3 是同一个逻辑,panic 忽略了上层的 defer
结论是:
1. 8 说的,panic 只处理当前协程的 defer 函数
2. panic 向上抛出