为什么我的`for-select`语句即使关闭后仍会继续从我的频道接收?

Ayu*_*pta -1 for-loop channel go

我有以下代码:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    ch2 := make(chan int)
    go func(c chan int, c2 chan int) {
        for {
            select {
            case v := <-c:
                fmt.Println(v)
            case v := <-c2:
                fmt.Println(v)
            default:
            }
        }
    }(ch, ch2)
    ch <- 1
    close(ch)
    close(ch2)
    time.Sleep(10 * time.Second)
}
Run Code Online (Sandbox Code Playgroud)

当我运行此命令时,它会打印1到标准输出,然后继续打印0。为什么是这样?

我知道我可以在goroutine中检查通道是否关闭,但是我只是想知道原因。

另外,假设我要在关闭所有(多个)通道后从goroutine退出,这可能吗?我以为一旦关闭了所有通道,在所有通道都关闭后,在默认情况下我有可能退出goroutine

icz*_*cza 5

这是每个Spec的预期行为:接收运算符:

封闭通道上的接收操作始终可以立即进行,在接收到任何先前发送的值之后,得出元素类型的零值

请参阅未初始化的通道的行为如何?

如果要在从通道接收到所有值(在关闭之前在通道上发送)后终止循环,请使用以下for ... range结构:

c := make(chan int) // Initialize some channel

for v := range c {
    fmt.Println("Received:", v)
}
Run Code Online (Sandbox Code Playgroud)

如果您有多个频道,并且希望从所有人那里接收,则可以使用多个goroutine,每个goroutine都有一个for range用于指定频道的。

另一个解决方案是:

一个功能可以从多个输入中读取并继续进行操作,直到将所有通道都多路复用到一个关闭所有输入都已关闭的通道上,然后关闭所有通道。这称为扇入。

Go Blog Go并发模式:管道和取消:Fan-out-fain-in中了解有关此内容的更多信息。