读取通道的不同方式

huangapple go评论83阅读模式
英文:

Difference Ways to Read Channels

问题

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

mychan := make(chan int)

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

这个goroutine无限地向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)
    }
}

// 现在每1秒打印1、foo、3、foo、5、foo...

尽管这个问题可能对一些人来说很琐碎,但如果有人能解释和启发我,我会非常感激。

英文:

I am curious of why different ways of reading values from a channel result in different behaviors. Provided the code:

mychan := make(chan int)

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

The goroutine "streams" a sequence of integer infinitely to a mychan channel. After this code, if you read directly with &lt;-mychan like so:

fmt.Println(&lt;-mychan)

This prints "0" as expected. If you keep repeating, it keeps reading:

fmt.Println(&lt;-mychan)    // 1
fmt.Println(&lt;-mychan)    // 2
//...

However, using the looping mechanism, it blocks infinitely.

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

That means that this mechanism can only reads from a closed channel, right?
However, with select method, thing gets wierder:

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

Now it prints alternately, like 1, 3, 5, 9, ... every 2s as if select was switching between mychan and some other invisible channel. Adding another case makes the case (no pun intended):

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

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

As trivial the question as it may seems to some of you, I'd appreciate if anyone can explain and enlighten me.

答案1

得分: 4

以下是翻译好的内容:

来自以下代码的行为:

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

似乎是在Go Playground上运行示例时的结果。当我在本地运行代码时,程序会无限制地每秒打印一个值。如果服务器上有一些代码分析工具可以判断代码是否会无限运行,那将很酷。

正如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)
    }
}
英文:

The behavior from

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

Appears to result from running examples on go playground When I ran the code locally the program prints out one value every second indefinitely. It would be cool if there is some code analysis tool they are using on the server to figure out if the code will run forever.

As Volker points out your second example is reading the channel twice per print statement. You can fix this with

for i:=0; i&lt;=10;i++ {
    select {
    case &lt;-time.After(1 * time.Second):
        fmt.Println(&quot;foo&quot;)
    case x, open := &lt;-mychan:
        if !open { return }
        fmt.Println(x)
    }
}

huangapple
  • 本文由 发表于 2016年12月22日 22:51:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/41285842.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定