在推迟功能内是否可以恐慌,特别是当它已经恐慌时?

csh*_*shu 5 go

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

func main(){
    defer func(){
        x:=recover()
        println(x.(int));
    }()
    sub()
}
Run Code Online (Sandbox Code Playgroud)

我尝试了这个代码,似乎第一次恐慌panic(1)被第二次恐慌简单地"覆盖"了panic(2).

但这样做可以吗?或者调用可能在延迟函数内发生恐慌的Golang函数?

(在C++中,从析构函数中抛出异常几乎是不可接受的.如果堆栈已经展开,它会终止程序.我不知道在Golang中是否会以类似的方式恐慌.)

icz*_*cza 5

是的,没关系.从延迟函数恐慌并不是一个新的特殊状态,它只是意味着恐慌序列不会停止.

您的示例代码也证明它没关系,甚至panic()来自延迟函数的调用也可以通过"上层"调用来停止recover().

规格:处理恐慌:

假设一个函数G推迟一个函数D调用,recover并且在G正在执行的同一个goroutine中的函数中发生恐慌.当延迟函数的运行到达时D,D调用的返回值recover将是传递给panic调用的值.如果D正常返回,没有开始新的恐慌,恐慌序列就会停止.在这种情况下,中间G调用的函数状态和对panic的调用被丢弃,并恢复正常执行.

这里需要注意的一点是,即使您调用panic()延迟函数,仍然会运行所有其他延迟函数.另外,panic()recover()使用延迟函数将"包裹"现有的恐慌,而不是"覆盖"它(尽管recover()调用只会返回传递给最后一次panic()调用的值).

看这个例子:

func main() {
    defer func() {
        fmt.Println("Checkpoint 1")
        panic(1)
    }()
    defer func() {
        fmt.Println("Checkpoint 2")
        panic(2)
    }()
    panic(999)
}
Run Code Online (Sandbox Code Playgroud)

输出(在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
Run Code Online (Sandbox Code Playgroud)

即使所有延迟函数都调用panic(),也会执行所有延迟函数,并且打印的最终恐慌序列包含传递给所有panic()调用的值.

如果您调用recover()延迟函数,您还会在最终打印输出中获得此"恢复"状态或信息:

defer func() {
    recover()
    fmt.Println("Checkpoint 1")
    panic(1)
}()
defer func() {
    recover()
    fmt.Println("Checkpoint 2")
    panic(2)
}()
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上试试):

Checkpoint 2
Checkpoint 1
panic: 999 [recovered]
    panic: 2 [recovered]
    panic: 1
...
Run Code Online (Sandbox Code Playgroud)