延期功能是否可以恐慌,特别是当它已经恐慌?

延期功能是否可以恐慌,特别是当它已经恐慌?

问题描述:

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是不好的。)

是的,这是好的。从延迟功能恐慌并不是一个真正的新的特殊状态,它只是意味着恐慌序列不会停止。

您的示例代码还证明它没问题,甚至从延迟函数调用的panic()都可以通过调用recover()的“上层”级别来停止。

Spec: Handling panics:

假设一个函数G推迟一个函数D调用recover并在其中被G执行相同够程的函数发生恐慌。当递延函数的运行达到D时,Drecover的调用的返回值将是传递给恐慌调用的值。 如果D正常返回,而不开始新的恐慌,则恐慌序列停止。在这种情况下,丢弃在G和调用恐慌之间调用的函数的状态,并恢复正常执行。

这里需要注意的一点是,即使您在延迟函数中调用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) 
} 

输出(尝试在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 
...