关于如何确保生成的 goroutine 在长时间运行的进程的上下文中正确“关闭”,我有一个基本的理解问题。我观看了有关该主题的讨论并阅读了有关最佳实践的内容。为了理解我的问题,请参阅视频“高级 Go 并发模式”here
对于以下内容,如果您在您的机器上运行代码,请导出环境变量 GOTRACEBACK=all
以便您能够在 panic 后看到例程状态。
我将原始示例的代码放在这里:naive (它不会在 go playground 上执行,我猜是因为使用了时间语句。请复制代码并在本地执行)
naive执行后panic的结果是
panic :给我看堆栈
协程 1 [正在运行]:
panic (0x48a680,0xc4201d8480)
/usr/lib/go/src/runtime/panic.go:500 +0x1a1
主.main()
/home/flx/workspace/go/go-rps/playground/ball-naive.go:18 +0x16b
goroutine 5 [chan receive]:
main.player(0x4a4ec4, 0x2, 0xc42006a060)
/home/flx/workspace/go/go-rps/playground/ball-naive.go:23 +0x61
由 main.main 创建
/home/flx/workspace/go/go-rps/playground/ball-naive.go:13 +0x76
goroutine 6 [chan receive]:
main.player(0x4a4ec6, 0x2, 0xc42006a060)
/home/flx/workspace/go/go-rps/playground/ball-naive.go:23 +0x61
由 main.main 创建
/home/flx/workspace/go/go-rps/playground/ball-naive.go:14 +0xad
退出状态 2
这证明了在系统上留下悬挂的 goroutines 的潜在问题,这对于长时间运行的进程尤其不利。
因此,根据我个人的理解,我尝试了两个稍微复杂一些的变体,这些变体可以在这里找到:
generator pattern with quit channel
(同样,在 playground 上不可执行,因为“处理时间太长”)
第一个解决方案由于各种原因不合适,甚至导致执行步骤的不确定性,具体取决于 goroutine 执行速度。
现在我想——问题终于来了! -- 使用退出 channel 的第二种解决方案适合在退出前从系统中消除所有执行痕迹。无论如何,“有时”程序退出得太快,并且 panic 报告一个额外的 goroutine runnable 仍然驻留在系统上。 panic 输出:
panic :显示堆栈
协程 1 [正在运行]:
panic (0x48d8e0,0xc4201e27c0)
/usr/lib/go/src/runtime/panic.go:500 +0x1a1
主.main()
/home/flx/workspace/go/go-rps/playground/ball-perfect.go:20 +0x1a9
goroutine 20 [可运行]:
main.player.func1(0xc420070060, 0x4a8986, 0x2, 0xc420070120)
/home/flx/workspace/go/go-rps/playground/ball-perfect.go:27 +0x211
由 main.player 创建
/home/flx/workspace/go/go-rps/playground/ball-perfect.go:36 +0x7f
退出状态 2
我的问题是:这不应该发生,对吧?在出现 panic 之前,我确实使用退出 channel 来清理状态。
我最后尝试在这里实现安全清理行为: artificial wait time for runnables to close
无论如何,该解决方案感觉不对,而且可能不适用于大量可运行对象?
确保正确清理的推荐和最惯用的模式是什么?
谢谢你的时间
最佳答案
你被输出愚弄了:你的“带退出 channel 的生成器模式”工作得很好,两个 goroutines 实际上 正确终止。
你在踪迹中看到它们是因为你太早 panic 了。记住:你必须让 goroutines 与 main 同时运行。 main 通过在 quit channel 上发出信号来“停止”这些 goroutines。在第 18 和 19 行的这两个发送之后,第 32 行的两个接收发生了。仅此而已!您仍然有三个 goroutines 正在运行:Main 在第 19 和 20 行之间,player goroutines 在第 32 和 33 行之间。如果现在 main 中的 panic 发生在 player 返回之前,那么 player goroutines 仍然存在并显示在 panic 中堆栈跟踪。如果只有调度程序有时间在第 33 行执行返回(它没有,因为你因 panic 而杀死它),这些 goroutine 会在几毫秒后结束。
这是“主要结束以尽早看到并发 goroutines 工作”问题的一个实例,这里每月问一次。您确实会看到并发的 goroutine 正在工作,但不是所有 都在工作。您可以尝试在 panic 之前休眠 2 毫秒,您的播放器 goroutine 将有时间执行返回,一切都很好。
关于go - 确保 goroutine 清理,最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39135348/