如何等待恐慌的goroutine?

时间:2019-01-21 13:39:29

标签: go

等待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。这是真的吗?如果是,我如何防止它发生?

2 个答案:

答案 0 :(得分:3)

111 Error in parsing request XML:Error: Datatype error: In element 'PhoneNumber' : Value '' does not match regular expression facet '.*[^\s].*'.. at line 36, column 35 将终止该进程,因为没有人从中恢复。如果要从goroutine中的紧急情况中恢复,则必须panic将调用堆栈包装在同一个goroutine中。

在这种情况下,将通过recover语句调用

wg.Done。但是无论如何,该过程可能在主goroutine完成defer之前就死了。

答案 1 :(得分:0)

@Eli Bendersky是正确的。

引用src/builtin/builtin.go

  

panic内置函数停止当前的正常执行   goroutine。当函数F调用恐慌时,F的正常执行停止   立即。在F中推迟执行的任何函数   按照通常的方式,然后F返回其调用方。对于呼叫者G,   然后,调用F就像调用恐慌一样,终止G的   执行并运行任何延迟的功能。这一直持续到所有   正在执行的goroutine中的函数已按相反的顺序停止。在   这时,程序终止,并报告错误情况,   包括引起恐慌的论点的价值。此终止顺序   称为恐慌,可以通过内置功能进行控制   恢复。

panic之后,将调用defer 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