一种常见的等待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条答案
按热度按时间wkyowqbh1#
无论如何,
panic
都会杀死这个进程,因为没有人会从这个进程中恢复过来。如果你想在一个goroutine中从崩溃中恢复过来,你必须让recover
在同一个goroutine中 Package 调用栈。wg.Done
将被defer
语句调用,但是进程可能在主goroutine完成wg.Wait
之前就已经死了。ttcibm8c2#
@伊莱·本德斯基是对的。
请参考src/builtin/builtin.go
内置函数panic会停止当前goroutine的正常执行。当函数F调用panic时,函数F的正常执行会立即停止。任何被函数F延迟执行的函数都会以正常的方式运行,然后函数F返回给调用者。对于调用者G来说,调用函数F就像调用panic一样。终止G的执行并运行所有被延迟的函数,直到执行中的goroutine中的所有函数都按相反的顺序停止为止,此时程序终止并报告错误状态。包括参数的值。这种终止序列称为panicing,可以由内置函数recover控制。
在
panic
之后,将调用defer
函数。在操场上检查这个:https://play.golang.org/p/yrXkEbE1Af7
输出量
那么你的第二个问题,“如何防止这种情况?”
如前所述,您可以在
panic
之后recover
Playground:https://play.golang.org/p/76pPrCVYN8u
输出量
ie3xauqp3#
通过添加
defer time.Sleep(time.Second)
可以引发不需要的行为:第一个
这个问题的解决方案是不延迟对
Done()
的调用,而将其放在末尾:在上面的代码中,没有对go例程行为实现细节的依赖性,并且在出现异常时总是失败。意识到这一点后,我注意到这个问题存在于几乎所有关于这个主题的教程中,这意味着我们正在寻找cargo cult programming的广泛表现。