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

Fal*_*len 5 concurrency go

如何优雅地处理waitgroup中的压缩?

换句话说,在下面的代码片段中,如何捕获gorourines调用方法的恐慌/粉碎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.")
}
Run Code Online (Sandbox Code Playgroud)

icz*_*cza 3

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

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

func do() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("Restored:", err)
        }
    }()
    str := "abc"
    fmt.Print(str[3])
}
Run Code Online (Sandbox Code Playgroud)

其次:仅此一项不会使您的代码正常工作,因为您调用了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.")
}
Run Code Online (Sandbox Code Playgroud)

输出(在Go Playground上尝试):

Restored: runtime error: index out of range
This line should be printed after all those invocations fail.
Run Code Online (Sandbox Code Playgroud)

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

传递WaitGroupdo()

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.")
}
Run Code Online (Sandbox Code Playgroud)

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