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

如何等待恐慌的协程?

如何等待恐慌的协程?

Go
富国沪深 2023-06-01 17:53:16
等待 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。这是真的吗?如果是,我该如何防止它发生?
查看完整描述

3 回答

?
LEATH

TA贡献1936条经验 获得超6个赞

无论如何都会panic终止进程,因为没有人从中恢复。如果你想在一个 goroutine 中从 panic 中恢复,你必须将recover调用堆栈包装在同一个 goroutine 中。

wg.Done在这种情况下,将由defer声明调用。但是这个过程可能会在主 goroutine 完成之前结束wg.Wait


查看完整回答
反对 回复 2023-06-01
?
MMTTMM

TA贡献1869条经验 获得超4个赞

参考src/builtin/builtin.go

panic 内置函数停止当前 goroutine 的正常执行。当函数 F 调用 panic 时,F 的正常执行会立即停止。任何被 F 延迟执行的函数都以通常的方式运行,然后 F 返回给它的调用者。对于调用者 G,调用 F 就像调用 panic,终止 G 的执行并运行任何延迟函数。这一直持续到执行 goroutine 中的所有函数都以相反的顺序停止。此时,程序终止并报告错误情况,包括 panic 的参数值。这种终止序列称为 panicing,可以通过内置函数 recover 来控制。

之后panicdefer将调用 func。

在操场上检查一下:https ://play.golang.org/p/yrXkEbE1Af7

package main


import (

    "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 panic

panic: Something unexpected happened.


goroutine 5 [running]:

main.main.func1(0x416020, 0x0)

    /tmp/sandbox946785562/main.go:17 +0x60

created by main.main

    /tmp/sandbox946785562/main.go:11 +0x80

那么你的第二个问题,“如何防止这种情况发生?”


如前所述,您可以recover在panic


游乐场:https://play.golang.org/p/76pPrCVYN8u


package main


import (

    "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.

0

1

2

3

4

5

6

7

8

9


查看完整回答
反对 回复 2023-06-01
?
吃鸡游戏

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

可以通过添加来引发不需要的行为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.exe

D:\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 时总是会失败。


查看完整回答
反对 回复 2023-06-01
  • 3 回答
  • 0 关注
  • 129 浏览
慕课专栏
更多

添加回答

举报

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