func sub(){
defer func (){
panic(2)
}()
panic(1)
}
func main(){
defer func(){
x:=recover()
println(x.(int));
}()
sub()
}
我试过这段代码,似乎第一次恐慌panic(1)
只是"被覆盖"第二次恐慌panic(2)
。
但这样做可以吗?或者调用一个可能在延迟函数内发生恐慌的Golang函数?
(在C ++中,从析构函数中抛出异常几乎是不可接受的。如果堆栈已经展开,它会终止程序。我不知道在Golang中是否会以类似的方式恐慌。)
答案 0 :(得分:5)
是的,没关系。从延迟函数恐慌并不是一个新的特殊状态,它只是意味着恐慌序列不会停止。
您的示例代码也证明它没问题,即使从延迟函数调用的panic()
也可以被" upper"等级调用recover()
。
假设函数
G
推迟调用D
的函数recover
,并且在G
正在执行的同一goroutine上的函数中发生恐慌。当延迟函数的运行达到D
时,D
对recover
的调用的返回值将是传递给恐慌调用的值。 如果D
正常返回,没有开始新的恐慌,恐慌序列就会停止。在这种情况下,G
之间调用的函数状态和对恐慌的调用将被丢弃,正常执行恢复。
这里需要注意的一点是,即使您在延迟函数中调用panic()
,仍然会运行所有其他延迟函数。此外,来自延迟函数的panic()
没有recover()
而不是" wrap"现有的恐慌,而不是"覆盖"它(虽然recover()
调用只会返回传递给最后一次panic()
调用的值,但这是真的。
见这个例子:
func main() {
defer func() {
fmt.Println("Checkpoint 1")
panic(1)
}()
defer func() {
fmt.Println("Checkpoint 2")
panic(2)
}()
panic(999)
}
输出(在Go Playground上尝试):
Checkpoint 2
Checkpoint 1
panic: 999
panic: 2
panic: 1
goroutine 1 [running]:
panic(0xfed00, 0x1040e140)
/usr/local/go/src/runtime/panic.go:500 +0x720
main.main.func1()
/tmp/sandbox284410661/main.go:8 +0x120
panic(0xfed00, 0x1040e0fc)
/usr/local/go/src/runtime/panic.go:458 +0x8a0
main.main.func2()
/tmp/sandbox284410661/main.go:12 +0x120
panic(0xfed00, 0x1040e0f8)
/usr/local/go/src/runtime/panic.go:458 +0x8a0
main.main()
/tmp/sandbox284410661/main.go:14 +0xc0
即使所有延迟函数都调用panic()
,所有延迟函数都会被执行,并且打印的最终恐慌序列包含传递给所有panic()
个调用的值。
如果你在延期功能中拨打recover()
,你也会得到这个"已恢复"最终打印输出中的状态或信息:
defer func() {
recover()
fmt.Println("Checkpoint 1")
panic(1)
}()
defer func() {
recover()
fmt.Println("Checkpoint 2")
panic(2)
}()
输出(在Go Playground上尝试):
Checkpoint 2
Checkpoint 1
panic: 999 [recovered]
panic: 2 [recovered]
panic: 1
...