以下代码会一直打印0
。
package main
import (
"fmt"
)
func main() {
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i // INPUT
}
close(c)
}()
for {
fmt.Print(<-c) // RECEIVER; OUTPUT
}
}
根据我的理解,它应该0
打印到9
,然后继续无限循环。为什么要继续打印zero
?
答案 0 :(得分:12)
你的理解(几乎)是正确的,除了主goroutine中的无限循环不会阻挡但是会无情地接收和打印。
你开始的goroutine会在频道上发送数字0,1,... 9,然后关闭它。并且从封闭信道接收不会阻塞,相反,它可以立即进行,并且它产生信道的元素类型的零值,对于类型0
是int
。这在Spec: Receive operator:
永远从
nil
频道接收阻止。 closed频道上的接收操作始终可以立即进行,在收到任何先前发送的值后产生元素类型zero value。
所以你确切地知道你应该做什么。首先它打印数字0..9
,然后非常快地打印0
(没有任何延迟),所以你可能甚至没有注意到最初的0..9
数字。
略微修改您的示例,以便在15次迭代后退出主goroutine中的循环将立即显示发生的情况:
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i // INPUT
}
close(c)
}()
for i := 0; i < 15; i++ {
fmt.Print(<-c) // RECEIVER; OUTPUT
}
输出(在Go Playground上尝试):
012345678900000
如果您的目标是在处理(打印)所有已发送的号码后退出,请使用频道上的for range
:
for i := range c {
fmt.Print(i) // RECEIVER; OUTPUT
}
如果您的目标是在处理完这10个号码后阻止新号码可用,则不要关闭该频道。这样下一个接收操作就会阻塞。在这种情况下,原始循环和for range
循环都可以正常工作,但for range
更好,因为如果/当频道关闭时总会退出。
查看此问题并记住频道公理:How does a non initialized channel behave?