猿问

为什么`defer recovery()` 没有捕捉到恐慌?

为什么调用defer func() { recover() }()成功恢复恐慌的 goroutine,但调用不成功defer recover()?


作为一个简约的例子,这段代码不会恐慌


package main


func main() {

    defer func() { recover() }()

    panic("panic")

}

然而,用recover直接替换匿名函数会导致恐慌


package main


func main() {

    defer recover()

    panic("panic")

}


噜噜哒
浏览 360回答 3
3回答

PIPIONE

引用内置函数的文档recover():如果在延迟函数之外调用恢复,它不会停止恐慌序列。在您的第二种情况下,recover()它本身就是延迟函数,显然 recover()不会调用自己。所以这不会停止恐慌序列。如果它自己recover()调用recover(),它会停止恐慌序列(但为什么会这样做?)。另一个有趣的例子:下面的代码也不会惊慌(在Go Playground上试试):package mainfunc main() {    var recover = func() { recover() }    defer recover()    panic("panic")}这里发生的事情是我们创建了一个recover函数类型的变量,它具有调用内置函数的匿名函数的值recover()。并且我们指定调用recover变量的值作为延迟函数,因此recover()从中调用内置函数会停止恐慌序列。

牧羊人nacy

该处理恐慌部分提到,两个内置函数panic和recover,帮助报告和处理运行时恐慌该recover函数允许程序管理恐慌 goroutine 的行为。假设一个函数G 延迟了一个D调用的函数,recover并且 apanic出现在G正在执行的同一个 goroutine 上的函数中。当延迟函数的运行达到 时D,D的调用返回值recover将是传递给恐慌调用的值。如果 D 正常返回,没有开始新的恐慌,恐慌序列就会停止。这说明这recover意味着在延迟函数中调用,而不是直接调用。当它恐慌时,“延迟函数”不能是内置函数recover(),而是在defer 语句中指定的函数。DeferStmt = "defer" Expression .表达式必须是函数或方法调用;不能用括号括起来。内置函数的调用与表达式语句一样受到限制。除了特定的内置函数,函数和方法调用和接收操作都可以出现在语句上下文中。

侃侃无极

一个观察是,这里真正的问题是设计,defer因此答案应该是这样的。激发这个答案,defer目前需要从 lambda 中获取一级嵌套堆栈,并且运行时使用此约束的特定副作用来确定是否recover()返回 nil。这是一个例子:func b() {  defer func() { if recover() != nil { fmt.Printf("bad") } }()}func a() {  defer func() {    b()    if recover() != nil {      fmt.Printf("good")    }  }()  panic("error")}将recover()在b()应返回零。在我看来,更好的选择是将defer函数 BODY 或块作用域(而不是函数调用)作为参数。在这一点上,panic和所述recover()返回值可以被绑定到特定堆栈帧,任何内堆栈帧将具有nilpancing上下文。因此,它看起来像这样:func b() {  defer { if recover() != nil { fmt.Printf("bad") } }}func a() {  defer {    b()    if recover() != nil {      fmt.Printf("good")    }  }  panic("error")}在这一点上,很明显它a()处于恐慌状态,但b()不是,并且不需要任何副作用,例如“位于延迟 lambda 的第一个堆栈帧中”,以正确实现运行时。因此,这里有悖常理:之所以没有按预期工作,是defer因为 go 语言中关键字的设计存在错误,这是使用不明显的实现细节副作用解决的,然后编码为这样的。
随时随地看视频慕课网APP

相关分类

Go
我要回答