【问题标题】:Ensuring goroutine cleanup, bestpractice确保 goroutine 清理,最佳实践
【发布时间】:2016-08-25 01:26:43
【问题描述】:

关于如何确保生成的 goroutine 在长时间运行的进程的上下文中正确“关闭”,我有一个基本的理解问题。我观看了有关该主题的演讲并阅读了有关最佳实践的信息。为了理解我的问题,请参考视频“高级 Go 并发模式”here

对于以下情况,如果您在您的机器上运行代码,请导出环境变量GOTRACEBACK=all,这样您就可以看到恐慌后的常规状态。

我把原始示例的代码放在这里:naive(它不会在go Playground上执行,我猜是因为使用了时间语句。请复制代码并在本地执行)

执行后naive实现的panic结果是

panic: show me the stacks goroutine 1 [running]: panic(0x48a680, 0xc4201d8480) /usr/lib/go/src/runtime/panic.go:500 +0x1a1 main.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 created by 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 created by main.main /home/flx/workspace/go/go-rps/playground/ball-naive.go:14 +0xad exit status 2

这说明了在系统上留下悬空的 goroutine 的潜在问题,这对于长时间运行的进程尤其不利。

因此,就我个人的理解而言,我尝试了两个稍微复杂的变体:

for-select with default

generator pattern with quit channel

(同样,不能在操场上执行,导致“处理时间过长”)

第一个解决方案由于各种原因不合适,甚至导致执行步骤的不确定性,具体取决于 goroutine 执行速度。

现在我想——问题终于来了! -- 使用退出通道的第二个解决方案适合在退出之前从系统中消除所有执行跟踪。无论如何,“有时”程序退出得太快,并且恐慌报告了一个额外的 goroutine 可运行仍然驻留在系统上。恐慌输出:

panic: show me the stacks goroutine 1 [running]: panic(0x48d8e0, 0xc4201e27c0) /usr/lib/go/src/runtime/panic.go:500 +0x1a1 main.main() /home/flx/workspace/go/go-rps/playground/ball-perfect.go:20 +0x1a9 goroutine 20 [runnable]: main.player.func1(0xc420070060, 0x4a8986, 0x2, 0xc420070120) /home/flx/workspace/go/go-rps/playground/ball-perfect.go:27 +0x211 created by main.player /home/flx/workspace/go/go-rps/playground/ball-perfect.go:36 +0x7f exit status 2

我的问题是:这不应该发生,对吧?在进入恐慌之前,我确实使用退出通道来清理状态。

我在这里做了最后一次实施安全清理行为的尝试: artificial wait time for runnables to close

无论如何,该解决方案感觉不对,也可能不适用于大量可运行文件?

为了确保正确清理,推荐和最惯用的模式是什么?

感谢您的宝贵时间

【问题讨论】:

  • 我无法理解该示例如何演示“程序完成执行后系统上的悬空 goroutines”。 Goroutines 是一个 Go 运行时概念 - 当程序完成执行时会被拆除。您看到的是输出,而拆解仍在进行中。如果在此之前清理它们..运行时无法报告发生了什么。还是我完全不理解你所说的?
  • 朴素示例中的例程无法退出,因为它们阻塞在共享通道上,等待球。视频中的重要部分是从约 4.40 分钟到约 5.10 分钟。程序本身会退出,但会留下悬空的例程。
  • 对,我很抱歉 - 我没有意识到您是在长时间运行的进程的上下文中说话,而不是在运行进程的实际终止。
  • 谢谢,我会编辑问题以便清楚!

标签: go concurrency stack-trace channel goroutine


【解决方案1】:

你被输出愚弄了:你的“带有退出通道的生成器模式”工作得很好,两个 goroutines 实际上 正确终止。

您在跟踪中看到它们是因为您过早恐慌。记住:你必须让 goroutine 与 main 并发运行。 main 通过在退出通道上发出信号来“停止”这些 goroutine。在第 18 行和第 19 行这两个发送之后,第 32 行上的两个接收已经发生。仅此而已!你仍然有三个 goroutines 在运行:Main 在第 19 和 20 行之间,而玩家 goroutines 在第 32 和 33 行之间。如果现在 main 中的恐慌发生在玩家返回之前,那么玩家 goroutine 仍然存在并且显示在恐慌中堆栈跟踪。如果只有调度程序有时间执行第 33 行的返回(因为你因为恐慌而杀死它,它没有这样做),这些 goroutine 会在几毫秒后结束。

这是“主要结束到早期看到并发 goroutines 工作”问题的一个实例,这里每月询问一次。你确实看到了一致的 goroutines 工作,但不是所有工作。您可以尝试在恐慌前 2 毫秒休眠,您的播放器 goroutine 将有时间执行返回,一切都很好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-19
    • 1970-01-01
    • 2013-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-14
    相关资源
    最近更新 更多