如何等待恐慌的协程?

等待 goroutine 的常见方法是使用*sync.WaitGroup:


func main() {

    wg := &sync.WaitGroup{}

    wg.Add(1)

    go func() {

        defer wg.Done()

        // Long running task

    }()

    wg.Wait()

}

这里没有问题。然而,这个呢:


func main() {

    wg := &sync.WaitGroup{}

    wg.Add(1)

    go func() {

        defer wg.Done()

        // Long running task

        panic("Something unexpected happened.")

    }()

    wg.Wait()

}

在这种情况下,当wg.Done()被调用时,我相信main()可以在没有panic()写入stdout/的细节的情况下退出stderr。这是真的吗?如果是,我该如何防止它发生?


富国沪深
浏览 130回答 3
3回答

LEATH

无论如何都会panic终止进程,因为没有人从中恢复。如果你想在一个 goroutine 中从 panic 中恢复,你必须将recover调用堆栈包装在同一个 goroutine 中。wg.Done在这种情况下,将由defer声明调用。但是这个过程可能会在主 goroutine 完成之前结束wg.Wait。

MMTTMM

参考src/builtin/builtin.gopanic 内置函数停止当前 goroutine 的正常执行。当函数 F 调用 panic 时,F 的正常执行会立即停止。任何被 F 延迟执行的函数都以通常的方式运行,然后 F 返回给它的调用者。对于调用者 G,调用 F 就像调用 panic,终止 G 的执行并运行任何延迟函数。这一直持续到执行 goroutine 中的所有函数都以相反的顺序停止。此时,程序终止并报告错误情况,包括 panic 的参数值。这种终止序列称为 panicing,可以通过内置函数 recover 来控制。之后panic,defer将调用 func。在操场上检查一下:https ://play.golang.org/p/yrXkEbE1Af7package mainimport (    "sync"    "fmt")func main() {    wg := &sync.WaitGroup{}    wg.Add(1)    go func() {        defer func(){            fmt.Println("expected to be called after panic")            wg.Done()        }()        // Long running task        panic("Something unexpected happened.")    }()    wg.Wait()}输出expected to be called after panicpanic: Something unexpected happened.goroutine 5 [running]:main.main.func1(0x416020, 0x0)    /tmp/sandbox946785562/main.go:17 +0x60created by main.main    /tmp/sandbox946785562/main.go:11 +0x80那么你的第二个问题,“如何防止这种情况发生?”如前所述,您可以recover在panic游乐场:https://play.golang.org/p/76pPrCVYN8upackage mainimport (    "sync"    "fmt")func main() {    wg := &sync.WaitGroup{}    wg.Add(1)    go func() {        defer func(){            if x:=recover();x!=nil{                fmt.Printf("%+v\n",x)            }            wg.Done()        }()        // Long running task        panic("Something unexpected happened.")    }()    wg.Wait()    for i:=0;i<10;i++{        fmt.Println(i)    }}输出Something unexpected happened.0123456789

吃鸡游戏

可以通过添加来引发不需要的行为defer time.Sleep(time.Second):func main() {    wg := &sync.WaitGroup{}    wg.Add(1)    go func() {        defer time.Sleep(time.Second)        defer wg.Done()        // Long running task        panic("Something unexpected happened.")    }()    wg.Wait()}D:\Projects\Code\Go\src\zyl\testexit>go build .D:\Projects\Code\Go\src\zyl\testexit>.\testexit.exeD:\Projects\Code\Go\src\zyl\testexit>echo %errorlevel%0该问题的解决方案是不延迟调用Done()并将其放在最后:func main() {    wg := &sync.WaitGroup{}    wg.Add(1)    go func() {        // Long running task which might panic        wg.Done()    }()    wg.Wait()}在上面的代码中,不存在对 go routine 行为的实现细节的依赖,并且在发生 panic 时总是会失败。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go