【问题标题】:Select statement in a for-loop reading from a closed channel gives zero values forever从封闭通道读取的 for 循环中的 Select 语句永远给出零值
【发布时间】:2018-05-20 07:49:33
【问题描述】:

鉴于此代码使用扇入模式模拟为 3 个 URL 获取一些网站内容,并在压缩通道上进行范围:https://play.golang.org/p/MSkRI7x4vz

for s := range r {
    println(s)
}

这很好用,但我想使用整体超时信号通道,所以我尝试在 for 循环中使用 select,如下所示: https://play.golang.org/p/LjDoIc0j-z

totalTimeout := time.After(300 * time.Millisecond)
loop:
    for {
        select {
        case s := <-r:
            fmt.Println(s)
        case <-totalTimeout: // signaling usage of a channel
            fmt.Println("Timed out")
            break loop
        }
    }
}

这表现不好:输入通道关闭后,来自扇入的压缩通道关闭。但是现在读取零值,直到超时有效发生。当我阅读http://www.tapirgames.com/blog/golang-channel 或规范https://golang.org/ref/spec#Close 时,似乎从封闭通道接收“永不阻塞”,因此返回零值。我只能猜测,一个范围检测到关闭,而选择不能,因为它的条件是从通道本身读取。

我可以这样爆发

case s := <-r:
    if s == "" {
        break loop
    }
    fmt.Println(s)

是否有另一种方法可以停止获取空值,或者这是在 for 循环中使用 select 的正确惯用方式?

【问题讨论】:

  • 接收(可以)返回两个值。使用第二个。

标签: for-loop go


【解决方案1】:

在接收到通道中的所有元素后,在关闭的通道上接收会产生零值。

使用两个值接收来检测通道何时关闭并从循环中中断。当通道因通道关闭而产生零值时,第二个值为 false。

for {
    select {
    case s, ok := <-r:
        if !ok {
            break loop
        }
        fmt.Println(s)
    case <-totalTimeout: // signaling usage of a channel
        fmt.Println("Timed out")
        break loop
    }
}

【讨论】:

  • 你是对的,逗号-ok 成语是这里的方式。我应该知道这一点,但并发性让我眼花缭乱。谢谢你:-)
  • @ChristofKälin,请参阅this 了解更多信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-23
  • 2017-09-22
  • 1970-01-01
  • 2016-09-08
  • 1970-01-01
  • 2019-08-31
相关资源
最近更新 更多