为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
噜噜哒 2021-09-27 17:45:31
为什么调用defer func() { recover() }()成功恢复恐慌的 goroutine,但调用不成功defer recover()?作为一个简约的例子,这段代码不会恐慌package mainfunc main() {    defer func() { recover() }()    panic("panic")}然而,用recover直接替换匿名函数会导致恐慌package mainfunc main() {    defer recover()    panic("panic")}
查看完整描述

3 回答

?
PIPIONE

TA贡献1829条经验 获得超9个赞

引用内置函数的文档recover():


如果在延迟函数之外调用恢复,它不会停止恐慌序列。


在您的第二种情况下,recover()它本身就是延迟函数,显然 recover()不会调用自己。所以这不会停止恐慌序列。


如果它自己recover()调用recover(),它会停止恐慌序列(但为什么会这样做?)。


另一个有趣的例子:


下面的代码也不会惊慌(在Go Playground上试试):


package main


func main() {

    var recover = func() { recover() }

    defer recover()

    panic("panic")

}

这里发生的事情是我们创建了一个recover函数类型的变量,它具有调用内置函数的匿名函数的值recover()。并且我们指定调用recover变量的值作为延迟函数,因此recover()从中调用内置函数会停止恐慌序列。


查看完整回答
反对 回复 2021-09-27
?
牧羊人nacy

TA贡献1862条经验 获得超7个赞

处理恐慌部分提到,

两个内置函数panicrecover,帮助报告和处理运行时恐慌

recover函数允许程序管理恐慌 goroutine 的行为。

假设一个函数G 延迟了一个D调用的函数recover并且 apanic出现在G正在执行的同一个 goroutine 上的函数中。

当延迟函数的运行达到 时DD的调用返回值recover将是传递给恐慌调用的值。
如果 D 正常返回,没有开始新的恐慌,恐慌序列就会停止。

这说明这recover意味着在延迟函数中调用,而不是直接调用。
当它恐慌时,“延迟函数”不能是内置函数recover(),而是在defer 语句中指定的函数。

DeferStmt = "defer" Expression .

表达式必须是函数或方法调用;不能用括号括起来。
内置函数的调用与表达式语句一样受到限制

除了特定的内置函数,函数和方法调用和接收操作都可以出现在语句上下文中。


查看完整回答
反对 回复 2021-09-27
?
侃侃无极

TA贡献2051条经验 获得超10个赞

一个观察是,这里真正的问题是设计,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 语言中关键字的设计存在错误,这是使用不明显的实现细节副作用解决的,然后编码为这样的。


查看完整回答
反对 回复 2021-09-27
  • 3 回答
  • 0 关注
  • 359 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信