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

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

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

Go
繁花不似锦 2021-11-29 15:44:40
如何优雅地处理等待组中的崩溃?换句话说,在下面的代码片段中,如何捕捉 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.")}
查看完整描述

2 回答

?
富国沪深

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

首先,注册一个延迟函数来恢复应该是函数中的第一行,因为因为你是最后做的,它甚至不会被到达,因为defer已经发生恐慌之前的行/代码所以延迟函数没有被注册哪个将恢复恐慌状态。


因此,将您的do()功能更改为:


func do() {

    defer func() {

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

            fmt.Println("Restored:", err)

        }

    }()

    str := "abc"

    fmt.Print(str[3])

}

第二:仅此一项不会使您的代码工作,因为您调用wg.Defer()了一个延迟函数,该函数只会在main()完成后运行- 这绝不是因为您调用wg.Wait()了main(). 所以wg.Wait()等待wg.Done()调用,但wg.Done()直到wg.Wait()返回才会运行调用。这是一个僵局。


您应该wg.Done()从do()函数调用,在延迟函数中,如下所示:


var wg sync.WaitGroup


func do() {

    defer func() {

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

            fmt.Println(err)

        }

        wg.Done()

    }()

    str := "abc"

    fmt.Print(str[3])

}


func main() {

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

        wg.Add(1)

        go do()

    }

    wg.Wait()

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

}

输出(在Go Playground上试试):


Restored: runtime error: index out of range

This line should be printed after all those invocations fail.

这当然需要将wg变量移动到全局范围。另一种选择是将其do()作为参数传递给。如果您决定采用这种方式,请注意您必须将指针传递给WaitGroup,否则只会传递一个副本(WaitGroup是一种struct类型)并且调用WaitGroup.Done()副本不会对原始文件产生影响。


随着传递WaitGroup到do():


func do(wg *sync.WaitGroup) {

    defer func() {

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

            fmt.Println("Restored:", err)

        }

        wg.Done()

    }()

    str := "abc"

    fmt.Print(str[3])

}


func main() {

    var wg sync.WaitGroup

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

        wg.Add(1)

        go do(&wg)

    }

    wg.Wait()

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

}

输出是一样的。在Go Playground上试试这个变体。


查看完整回答
反对 回复 2021-11-29
?
喵喵时光机

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

出色地解释了如何正确使用WaitGroup及其功能Wait和Done


我喜欢WaitGroup简单。但是,我不喜欢我们需要传递对 goroutine 的引用,因为这意味着并发逻辑将与您的业务逻辑混合。


所以我想出了这个通用函数来为我解决这个问题:


// Parallelize parallelizes the function calls

func Parallelize(functions ...func()) {

    var waitGroup sync.WaitGroup

    waitGroup.Add(len(functions))


    defer waitGroup.Wait()


    for _, function := range functions {

        go func(copy func()) {

            defer waitGroup.Done()

            copy()

        }(function)

    }

}

所以你的例子可以这样解决:


func do() {

    defer func() {

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

            fmt.Println(err)

        }

    }()


    str := "abc"

    fmt.Print(str[3])

}


func main() {

    Parallelize(do, do, do)


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

}

如果你想使用它,你可以在这里找到它https://github.com/shomali11/util


查看完整回答
反对 回复 2021-11-29
  • 2 回答
  • 0 关注
  • 146 浏览
慕课专栏
更多

添加回答

举报

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