如果另一个 goroutines 崩溃,你如何保持 goroutines 运行?

问题描述

我一直试图找到可以回答这个问题的东西,但我找不到任何可以谈论它的东西。

假设我在 Go 中有一个类似这样的函数

func main() {
    // assume this wrapped in a waitgroup or something 
    // so that it doesnt exit
    go queue.ConsumeAndDoSomething()
    go api.StartServer()
}

在这里有两个 goroutines 做完全不同的事情,如果另一个 goroutine 崩溃/恐慌,理想情况下应该继续运行。如果队列操作失败,API 服务器应该会受到影响,反之亦然。

我不确定这是否可能(甚至推荐)。有没有一种干净的方法可以做到这一点,还是应该在 goroutine 恐慌时整个程序退出

解决方法

您必须使用内置的 recover() 函数从恐慌中恢复,并且必须在 deferred 函数中调用它。

假设您有一个可能会恐慌的函数:

func doPanic() {
    log.Println("about to panic")
    panic("test")
}

创建一个辅助函数来启动一个作为“受保护”的 goroutine 的函数(免受恐慌):

func protect(f func()) {
    defer func() {
        if err := recover(); err != nil {
            log.Printf("Recovered: %v",err)
        }
    }()

    f()
}

像这样使用它:

func main() {
    go protect(doPanic)

    for {
        time.Sleep(time.Second)
        fmt.Println("tick")
    }
}

此测试应用将输出:

2021/03/04 14:12:31 about to panic
2021/03/04 14:12:31 Recovered: test
tick
tick
tick
...

查看相关问题:Generic panic recovering in go programs

,

如果您希望其他 goroutine 不受影响,则每个 goroutine 都必须推迟 recover 调用以从潜在的恐慌中恢复。

代替

go queue.ConsumeAndDoSomething()

你应该使用

go func(){
    defer func() {
            if r := recover(); r != nil {
                log.Error("goroutine paniqued: ",r)
            }
        }()
    queue.ConsumeAndDoSomething()
}()