【问题标题】:Strange select-case-default behavior [closed]奇怪的选择案例默认行为[关闭]
【发布时间】:2020-05-17 21:03:07
【问题描述】:

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

【问题讨论】:

  • 不要发布(链接到)屏幕截图,以文本形式发布实际代码。

标签: go goroutine


【解决方案1】:

您的代码包含潜在的死锁,如果您的非默认情况足够慢(例如,在 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
...

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

【讨论】:

  • 感谢您先生的完整回答。
【解决方案2】:

你应该只检查你是否在循环条件下完成:

for i1 < 10 || i2 < 10 {
    select {
    case x, ok := <-c1:
        if ok {
            fmt.Printf("ch1->%v i= %v\n", x, i1)
            tre1[i1] = x
            i1++
        }
    case y, ok := <-c2:
        if ok {
            fmt.Printf("ch2->%v i= %v\n", y, i2)
            tre2[i2] = y
            i2++
        }
    }
}

【讨论】:

  • 我询问 printf 对我的 select-case 的影响,而不是改进。
猜你喜欢
  • 1970-01-01
  • 2013-06-01
  • 2012-12-23
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 2013-01-29
  • 2014-05-28
  • 1970-01-01
相关资源
最近更新 更多