func sub(){
    defer func (){
        panic(2)
    }()
    panic(1)
}

func main(){
    defer func(){
        x:=recover()
        println(x.(int));
    }()
    sub()
}

我尝试了这段代码,似乎第一个 panic panic(1)被第二个 panic panic(2)完全“覆盖”了。

但是可以吗?还是调用一个可能在defer函数内部 panic 的Golang函数?

(在C++中,从析构函数中抛出异常几乎是 Not Acceptable 。如果堆栈已经解散,则终止程序。我想知道在Golang中以类似方式 panic 是否会很糟糕。)

最佳答案

是的,没关系。从延迟功能进行 panic 并不是真正的新的特殊状态,它只是意味着 panic 序列不会停止。

您的示例代码还证明还可以,甚至可以通过对 panic() 的“上级”调用来停止从延迟函数调用的 recover()

Spec: Handling panics:



这里要注意的一件事是,即使您在延迟函数中调用panic(),其他所有延迟函数仍将运行。同样,没有来自延迟函数的panic()recover()宁愿“包装”现有的 panic ,而不是“覆盖”它(尽管确实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
...

关于go - 可以在defer函数内部 panic ,特别是当它已经 panic 时,可以 panic 吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41139447/

10-10 10:34