代码之家  ›  专栏  ›  技术社区  ›  smac89

如果通道关闭,带有读取操作的Select case不会阻塞,而是返回空值[重复]

  •  0
  • smac89  · 技术社区  · 1 年前

    给定以下代码,该代码模拟了在压缩通道上使用扇入模式和范围为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的正确惯用方法?

    0 回复  |  直到 7 年前
        1
  •  10
  •   Cerise Limón    7 年前

    在接收到信道中的所有元素之后,在闭合信道上的接收产生零值。

    使用两个值的receive来检测通道何时关闭以及何时脱离循环。当通道由于通道关闭而产生零值时,第二个值为假。

    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
        }
    }