go - 确保 goroutine 清理,最佳实践

标签 go concurrency stack-trace channel goroutine

关于如何确保生成的 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 的潜在问题,这对于长时间运行的进程尤其不利。

因此,根据我个人的理解,我尝试了两个稍微复杂一些的变体,这些变体可以在这里找到:

for-select with default

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/

相关文章:

Go io阅读器包装器

go - 使用 Go 包中的私有(private) "lowercase"函数

java - 不可思议的堆栈跟踪

go - 如何检查 golang 二进制文件是否使用 --ldflags ="-s -w"编译

concurrency - 在达到超时之前如何读取 UDP 连接?

python - Django select_for_update 可以用来获取读锁吗?

c - 检测两个输入引脚上的两个同时信号的最佳实践

java stacktrace 显示一个阻塞的线程,但没有关于阻塞它的信息

stack-trace - 如何获取 Java 中当前的堆栈跟踪?

struct - Golang - 嵌套结构中的 slice