如何从等待组调用的函数中捕获运行时错误?

如何优雅地处理等待组中的崩溃?


换句话说,在下面的代码片段中,如何捕捉 goroutines 调用方法的恐慌/崩溃do()?


func do(){

    str := "abc"

    fmt.Print(str[3])

    defer func() {

        if err := recover(); err != nil {

            fmt.Print(err)

        }

    }()

}


func main() {

    var wg sync.WaitGroup


    for i := 0; i < 1; i++ {

        wg.Add(1)

        go do()

        defer func() {

            wg.Done()

            if err := recover(); err != nil {

                fmt.Print(err)

            }

        }()

    }

    wg.Wait()

    fmt.Println("This line should be printed after all those invocations fail.")

}


繁花不似锦
浏览 147回答 2
2回答

富国沪深

首先,注册一个延迟函数来恢复应该是函数中的第一行,因为因为你是最后做的,它甚至不会被到达,因为defer已经发生恐慌之前的行/代码所以延迟函数没有被注册哪个将恢复恐慌状态。因此,将您的do()功能更改为:func do() {&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; if err := recover(); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Restored:", err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }()&nbsp; &nbsp; str := "abc"&nbsp; &nbsp; fmt.Print(str[3])}第二:仅此一项不会使您的代码工作,因为您调用wg.Defer()了一个延迟函数,该函数只会在main()完成后运行- 这绝不是因为您调用wg.Wait()了main(). 所以wg.Wait()等待wg.Done()调用,但wg.Done()直到wg.Wait()返回才会运行调用。这是一个僵局。您应该wg.Done()从do()函数调用,在延迟函数中,如下所示:var wg sync.WaitGroupfunc do() {&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; if err := recover(); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; wg.Done()&nbsp; &nbsp; }()&nbsp; &nbsp; str := "abc"&nbsp; &nbsp; fmt.Print(str[3])}func main() {&nbsp; &nbsp; for i := 0; i < 1; i++ {&nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; go do()&nbsp; &nbsp; }&nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; fmt.Println("This line should be printed after all those invocations fail.")}输出(在Go Playground上试试):Restored: runtime error: index out of rangeThis line should be printed after all those invocations fail.这当然需要将wg变量移动到全局范围。另一种选择是将其do()作为参数传递给。如果您决定采用这种方式,请注意您必须将指针传递给WaitGroup,否则只会传递一个副本(WaitGroup是一种struct类型)并且调用WaitGroup.Done()副本不会对原始文件产生影响。随着传递WaitGroup到do():func do(wg *sync.WaitGroup) {&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; if err := recover(); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("Restored:", err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; wg.Done()&nbsp; &nbsp; }()&nbsp; &nbsp; str := "abc"&nbsp; &nbsp; fmt.Print(str[3])}func main() {&nbsp; &nbsp; var wg sync.WaitGroup&nbsp; &nbsp; for i := 0; i < 1; i++ {&nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; go do(&wg)&nbsp; &nbsp; }&nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; fmt.Println("This line should be printed after all those invocations fail.")}输出是一样的。在Go Playground上试试这个变体。

喵喵时光机

出色地解释了如何正确使用WaitGroup及其功能Wait和Done我喜欢WaitGroup简单。但是,我不喜欢我们需要传递对 goroutine 的引用,因为这意味着并发逻辑将与您的业务逻辑混合。所以我想出了这个通用函数来为我解决这个问题:// Parallelize parallelizes the function callsfunc Parallelize(functions ...func()) {&nbsp; &nbsp; var waitGroup sync.WaitGroup&nbsp; &nbsp; waitGroup.Add(len(functions))&nbsp; &nbsp; defer waitGroup.Wait()&nbsp; &nbsp; for _, function := range functions {&nbsp; &nbsp; &nbsp; &nbsp; go func(copy func()) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer waitGroup.Done()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; copy()&nbsp; &nbsp; &nbsp; &nbsp; }(function)&nbsp; &nbsp; }}所以你的例子可以这样解决:func do() {&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; if err := recover(); err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }()&nbsp; &nbsp; str := "abc"&nbsp; &nbsp; fmt.Print(str[3])}func main() {&nbsp; &nbsp; Parallelize(do, do, do)&nbsp; &nbsp; fmt.Println("This line should be printed after all those invocations fail.")}如果你想使用它,你可以在这里找到它https://github.com/shomali11/util
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go