我可以从恐慌中恢复,处理错误,然后再次恐慌并保持原始堆栈跟踪?

use*_*610 7 error-handling stack-trace go panic

是否有可能"重新抛出"错误recover并保留原始堆栈跟踪?我知道如何做的最好的事情就是再次恐慌,但这确实会创建一个新的堆栈跟踪.

func do() {
    defer func() {
        cleanUp()
        if x := recover(); x != nil {
            handleError()
            panic(x)
        }
    }()
    doStuff()
}
Run Code Online (Sandbox Code Playgroud)

我想要这个的动机是,除非我的函数正常退出或handleError运行,否则我的程序会死锁.除非我保留原始的痕迹,否则我不知道它在哪里坠毁.

use*_*610 7

解决方案是不调用recover,因为既不能重新抛出也不能访问堆栈跟踪.使用bool标志而不是recover检查恐慌.

https://play.golang.org/p/PKeP9s-3tF

func do() {
    panicked := true
    defer func() {
        cleanUp()
        if panicked {
            handleError()
        }
    }()
    doStuff()
    panicked = false
}
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这种方法意味着你不应该在`do()`中的任何地方返回,除了最后,在'panicked = false`下面.或者你必须在任何其他返回语句之前显式设置`panicked = false`,这很容易出错.以后修改此函数的任何人都需要理解这个或者将延迟函数中的逻辑回归.我希望`恐慌`可以像Java或C#一样"重新抛出"以保留原始堆栈跟踪而不诉诸于此. (3认同)

Fil*_*und 6

即使不调用,堆栈中较高的延迟函数也会在恐慌时运行recover().

只需删除if语句和重新恐慌.然后处理你的错误,让恐慌继续上升.

func do() {
    defer handleError()
    doStuff()
}
Run Code Online (Sandbox Code Playgroud)

一个简单的演示:

https://play.golang.org/p/UiRou5MhUR

func a() {
    defer func() {
        fmt.Println("a")
    }()
    panic("test")
}
func b() {
    defer func() {
        fmt.Println("b")
    }()
}

func main() {
    fmt.Println("Hello, playground")
    b()
}
Run Code Online (Sandbox Code Playgroud)

输出

Hello, playground
b
Run Code Online (Sandbox Code Playgroud)

  • 然后你可以设置一个名为"succ"的布尔变量,最初设置为false,并在函数末尾设置为true.如果设置了succ,则不要调用handleError. (2认同)