首页 > 解决方案 > 奇怪的选择案例默认行为

问题描述

有人可以解释我奇怪的选择案例默认行为吗?如果我将 fmt.printf(something %v\n) 放在案例中,它永远不会达到默认阶段并超时。但如果我推迟或评论 printf 没关系。截图:错误代码 被延迟评论 ,我在操场上的代码https://play.golang.org/p/FYsToHUJE43

标签: gogoroutine

解决方案


您的代码包含潜在的死锁,如果您的非默认情况足够慢(例如,在 IO 操作的情况下),它最终会发生。

问题是,如果一个通道关闭,那么从该特定通道读取会产生一nil, false对。如果您的两个通道都关闭,那么您将永远不会遇到默认情况,因为您的选择将在两个失败的读取之间交替,从而导致无限循环。

如果您注释掉这些fmt.Printf()函数,您的 for 循环将会非常快,以至于它可以(但不能保证!)在您收集足够的树项之后,但在Walk()函数关闭通道之前进入一个循环,从而进入默认情况并中断循环。我试图举一个可能的执行顺序的例子,它将在下面完成它的运行。

\\ t1 - thread of Walk(tree1)
\\ t2 - thread of Walk(tree2)
\\ t3 - thread of the loop

t1 > c.send()
t2 > c.send()
t3 > handle c1
t3 > handle c2
t3 > default, break
t1 > c.close()
t2 > c.close()

但是,放回 IO 操作会大大减慢您的 for 循环,以至于在两个通道关闭之前它没有机会进入 select 语句,因此它会陷入无限循环。现在发生的事情是这样的:

t1 > c.send()
t2 > c.send()
t3 > handle c1 // long IO operation
t1 > c.close()
t3 > handle c2 // long IO operation
t2 > c.close()
t3 > handle c1 with error
t3 > handle c1 with error
t3 > handle c2 with error
...

您应该在您的选择语句之外处理您的中断条件。


推荐阅读