阅读频道的不同方式

Difference Ways to Read Channels

我很好奇为什么从通道读取值的不同方式会导致不同的行为。提供代码:

mychan := make(chan int)

go func() {
    i := 0
    for {
        mychan <- i
        <-time.After(time.Second * 1)
        i++
    }
}()

goroutine "streams" 一个无限的整数序列到一个 mychan 通道。在这段代码之后,如果你像这样直接用 <-mychan 读取:

fmt.Println(<-mychan)

这会按预期打印“0”。如果你继续重复,它会继续阅读:

fmt.Println(<-mychan)    // 1
fmt.Println(<-mychan)    // 2
//...

但是,使用循环机制,它会无限阻塞。

for i := range mychan {
    fmt.Println(i)
}

也就是说这个机制只能从一个关闭的通道中读取,对吧? 然而,使用 select 方法,事情变得更加复杂:

for i:=0; i<=10;i++ {
    select {
    case <-mychan:
        fmt.Println(<-mychan)
    }
}

现在它交替打印,例如 1、3、5、9,... 每 2 秒就好像 selectmychan 和其他一些不可见通道之间切换一样。添加另一个 case 就可以了(没有双关语意):

for i:=0; i<=10;i++ {
    select {
    case <-time.After(1 * time.Second):
        fmt.Println("foo")
    case <-mychan:
        fmt.Println(<-mychan)
    }
}

// now prints 1, foo, 3, foo, 5, foo, ... every 1s

对于你们中的一些人来说,这个问题似乎微不足道,如果有人能解释和启发我,我将不胜感激。

来自

的行为
for i := range mychan {
    fmt.Println(i)
}

似乎是 go playground 上的 运行ning 示例的结果当我 运行 本地代码时,程序无限期地每秒打印一个值。如果他们在服务器上使用一些代码分析工具来确定代码是否会 运行 永远,那就太好了。

正如 Volker 指出的那样,您的第二个示例是每个打印语句读取频道两次。您可以使用

解决此问题
for i:=0; i<=10;i++ {
    select {
    case <-time.After(1 * time.Second):
        fmt.Println("foo")
    case x, open := <-mychan:
        if !open { return }
        fmt.Println(x)
    }
}