确保goroutine清理,最佳实践

时间:2016-08-25 01:26:44

标签: go concurrency stack-trace channel goroutine

我有一个基本的理解问题,关于如何确保产生的goroutine已经关闭"适当地在长期运行的过程中。我观看了有关该主题的讨论并阅读了有关最佳实践的内容。为了解我的问题,请参阅视频" Advanced Go Concurrency Patterns" here

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

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

执行后天真实施的恐慌结果是

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 runnable。恐慌输出:

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

无论如何,这种解决方案感觉不对,也可能不适用于大量的可运行的游戏?

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

感谢您的时间

1 个答案:

答案 0 :(得分:1)

你被输出所欺骗:你的"发生器模式与退出通道"工作得很好,两个goroutine实际 正确终止。

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

这是早期看到并发goroutine工作的主要目的的实例"问题每月一次在这里问。你确实看到了同样的goroutines正在工作,但没有所有工作。你可能会在恐慌之前尝试睡2毫秒,你的玩家goroutines将有时间执行返回,一切都很好。