我有两个goroutines独立产生数据,每个将它们发送到一个 channel 。在我的主要goroutine中,我想在输入这些输出时使用它们,但是不在乎它们的输入顺序。每个 channel 在耗尽其输出时都会关闭。虽然select语句是用于像这样独立使用输入的最佳语法,但是直到两个 channel 都关闭之前,我还没有看到一种简洁的循环方法。

for {
    select {
    case p, ok := <-mins:
        if ok {
            fmt.Println("Min:", p) //consume output
        }
    case p, ok := <-maxs:
        if ok {
            fmt.Println("Max:", p) //consume output
        }
    //default: //can't guarantee this won't happen while channels are open
    //    break //ideally I would leave the infinite loop
                //only when both channels are done
    }
}

我能想到的最好的方法是以下操作(只是草绘,可能会有编译错误):
for {
    minDone, maxDone := false, false
    select {
    case p, ok := <-mins:
        if ok {
            fmt.Println("Min:", p) //consume output
        } else {
            minDone = true
        }
    case p, ok := <-maxs:
        if ok {
            fmt.Println("Max:", p) //consume output
        } else {
            maxDone = true
        }
    }
    if (minDone && maxDone) {break}
}

但是,如果您使用的 channel 超过两个或三个,这看起来将变得站不住脚。我知道的唯一另一种方法是在switch语句中使用一个timout案例,该案例可能足够小以至于有可能提前退出,或者将太多的停机时间注入(inject)到最终循环中。有没有更好的方法来测试 channel 是否在select语句中?

最佳答案

您的示例解决方案将无法正常工作。一旦其中之一关闭,它将始终可以立即进行通信。这意味着您的goroutine将永远不会屈服,而其他 channel 可能永远也不会就绪。您将有效地进入无限循环。我在此处发布了一个示例来说明效果:http://play.golang.org/p/rOjdvnji49

那么,我该如何解决这个问题呢?零 channel 永远不会准备好进行通信。因此,每次遇到封闭 channel 时,您都可以将该 channel 设为零,以确保不再选择该 channel 。此处的可运行示例:http://play.golang.org/p/8lkV_Hffyj

for {
    select {
    case x, ok := <-ch:
        fmt.Println("ch1", x, ok)
        if !ok {
            ch = nil
        }
    case x, ok := <-ch2:
        fmt.Println("ch2", x, ok)
        if !ok {
            ch2 = nil
        }
    }

    if ch == nil && ch2 == nil {
        break
    }
}

至于担心它变得笨拙,我认为它不会。很少有 channel 一次转到太多地方的情况很少。这很少出现,我的第一个建议就是解决这个问题。较长的if语句将10个 channel 与nil进行比较并不是尝试在选择中处理10个 channel 的最糟糕的部分。

关于go - 当所有 channel 都关闭时中断选择语句,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13666253/

10-12 15:21
查看更多