奇怪的选择大小写默认行为 [关闭]。

问题描述 投票:0回答:1

谁能给我解释一下奇怪的select-case-default行为?如果我把fmt.printf(something %v/zn)放在case中,它永远不会达到默认阶段,并且会超时。但如果我推迟或注释printf,它就会正常。截图。坏代码 评论 迟延 和我的代码在O游乐场 https:/play.golang.orgpFYsToHUJE43。

go goroutine
1个回答
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
...

你应该在select语句之外处理你的break条件。


0
投票

你应该只检查你是否在循环条件中完成。

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++
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.