尝试实现死锁时无限循环零

时间:2017-09-30 09:25:10

标签: go channel

以下代码会一直打印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

1 个答案:

答案 0 :(得分:12)

你的理解(几乎)是正确的,除了主goroutine中的无限循环不会阻挡但是会无情地接收和打印。

你开始的goroutine会在频道上发送数字0,1,... 9,然后关闭它。并且从封闭信道接收不会阻塞,相反,它可以立即进行,并且它产生信道的元素类型的零值,对于类型0int。这在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?