go运行时可以检测panic(nil)
并报告错误。
但是,我无法在defer
red函数中检测到panic(nil)
和recover()
,因为它返回nil
,所以我无法将其与正常执行(无异常)区分开来,因为我将测试recover()
的返回值是否为nil。
例如,
defer func(){
var err = recover()
if err != nil {
// Real serious situation. Panic from inner code.
// And we may have some critical resources which
// must be cleaned-up at any cases.
// However, this will not be executed for panic(nil)
rollback()
// I am still not sure that how should I treat `panic`…
// Should I just ignore them?
}
}()
var err = doTransaction()
if err == nil {
commit() // Happy case.
} else {
rollback() // Regular execution. Just a lucky case.
}
ROLLBACK只是一个例子,我想我可以有很多关键的情况下需要清理。嗯,这些清理代码不会执行真正的程序崩溃太多,但我想尽可能多的防御。
如何检测延迟函数中的任何异常,而不管其参数是什么?
4条答案
按热度按时间erhoui1w1#
除非我误解了您的问题,否则延迟函数调用 * 将 * 在死机时运行,即使传递的值是
nil
。因此,通过比较
recover()
和nil
返回的值,您可以轻松地检测panic(nil)
是否发生。编辑以回答注解:
是的,那是真的;延迟调用通常会在函数返回时运行,但在
panic()
之后展开调用堆栈时也会运行。更新问题后编辑:
你说的对,没有办法区分这些情况,另一方面,对
nil
的恐慌也没有多大意义--特别是因为这个限制。我能想到的
panic(nil)
的唯一用例是故意避免恢复,并通过堆栈跟踪强制程序崩溃,不过还有更优雅的方法可以做到这一点,例如使用runtime
包。byqmnocz2#
我可以在退出前设置一个标志。
好吧,panic是goroutine特有的,而且一个goroutine只能在一个线程中运行,所以在变量
ok
周围不需要同步/锁,如果我说错了,请纠正我。r8uurelv3#
大多数情况下,
if recover() != nil
的惯用方法是有效的,但是为了健壮性,或者在执行第三方代码时,不应该使用它,因为它不检测panic(nil)
。下面的模式是您可以检查它的方法:
如果
action()
死机,那么下面的行将永远不会执行,panicked
将保持为真。您可以使用上面的helper函数进行检查,如下所示:或者,您可以直接将模式实现到要检查的函数中,但这样做可能会变得更混乱。
oogrdqng4#
检查proposal 25448“规格:保证从recover返回非空值”会有所帮助。
在Go语言1中允许使用nil panic值调用panic,但是很奇怪。
几乎所有代码都使用以下命令检查死机:
...这在
panic(nil)
的情况下是不正确的。正确的方式更像是:
建议:让运行时
panic
函数将其panic值从nil
提升为类似于runtime.NilPanic
的私有、不可赋值类型的全局值:2018年提出,刚刚获得受理(2023年1月)
目前的建议是,从Go语言1.21开始(比如说),
panic(nil)
在panic
期间变成panic(&runtime.PanicNil{})
(所以一个恰好是nil
接口的变量也会这样做,而不仅仅是文本panic(nil)
)。panic(nil)
始终为OK,但在此更改之后,recover()
返回&runtime.PanicNil{}
而不是nil
。如果为
GODEBUG=panicnil=1
,则禁用此更改,并且panic(nil)
会使recover()
返回nil
,因为它始终如此。假设#56986(“建议:Go语言的扩展向后兼容性”)也发生在Go语言1.21中,这种行为只会在工作模块(顶层
go.mod
)中的“go 1.21
”模块中发生变化。因此,当您从Go语言1.20升级到Go语言1.21时,如果不更改任何
go.mod
行,您仍然会得到旧的panic(nil)
行为。当你把你的顶层go.mod修改为
go 1.21
时,你就会在整个程序中得到新的行为。