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

Infinite loop with zeros while trying to achieve deadlock

下面的代码一直打印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
    }
}

根据我的理解,它应该打印 09 然后继续无限循环。为什么它一直打印 zero?

你的理解(几乎)是正确的,只是主goroutine中的无限循环不会阻塞而是会不停地接收和打印。

您启动的 goroutine 在通道上发送数字 0、1、... 9,然后关闭它。并且从关闭的通道接收不会阻塞,相反,它可以立即进行,并且它产生通道的元素类型的零值,对于类型 int0 。这在 Spec: Receive operator:

中说明

Receiving from a nil channel blocks forever. A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.

所以你看到了你应该看到的。首先它打印数字 0..9,然后非常快速地打印 0(没有任何延迟),所以您可能甚至没有注意到初始的 0..9 数字。

稍微修改您的示例,使主 goroutine 中的循环在 15 次迭代后退出,将立即显示发生的情况:

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 更好,因为如果 / 当通道关闭时它总是退出。

看看这道题,记住通道公理: