为什么通道没有关闭?

Why channel does not get closed?

对于下面的代码:

package main

import "fmt"

func emit(c chan string) {
    words := []string{"The", "quick", "brown", "fox", "ran", "away"}

    for _, word := range words {
        fmt.Printf("send %s\n", word)
        c <- word
    }
    fmt.Printf("Close the channel\n")
    close(c)
}

func main() {
    wordChannel := make(chan string)

    go emit(wordChannel)

    word := <-wordChannel
    fmt.Printf("receive %s \n", word)

    word = <-wordChannel
    fmt.Printf("receive %s \n", word)

    word = <-wordChannel
    fmt.Printf("receive %s \n", word)

    word, ok := <-wordChannel
    fmt.Printf("receive %s %t\n", word, ok)

    word, ok = <-wordChannel
    fmt.Printf("receive %s %t\n", word, ok)

    word, ok = <-wordChannel
    fmt.Printf("receive %s %t\n", word, ok)

}

下面是输出:

send The
send quick
receive The 
receive quick 
send brown
send fox
receive brown 
receive fox true
send ran
send away
receive ran true
receive away true

为什么sender go-routine不关闭通道?

您的 main goroutine 结束时没有与 emit goroutine 协调以知道它已完成。当到达 main 的末尾时,程序及其所有 goroutines 都会结束,无论是否有任何 go routines 可能仍在处理中,除非您明确等待它们。

关闭通道 可以 帮助将 emit goroutine 的完成传达给 main。在这种情况下,并使用来自通道的二值响应读取如

word, ok = <-wordChannel

确实 将通道的状态(打开或关闭)公开给 main,但您永远不会用它来控制 main 的流程。此外,您有一个 精确 读取 硬编码 到 main 中的次数。因此,即使您 通过通道关闭控制流量,您也永远不会尝试最终读取以查看关闭的通道。

幸运的是,go 的解决方案非常简单。 range通过 go 通道将读取值,直到通道关闭。所以你可以简化你的代码,删除显式的通道接收数量,并使用通道关闭来表示 emit 的完成,所有这些都使用这个更简洁的版本:

package main

import "fmt"

func emit(c chan string) {
    words := []string{"The", "quick", "brown", "fox", "ran", "away"}

    for _, word := range words {
        fmt.Printf("send %s\n", word)
        c <- word
    }
    fmt.Printf("Close the channel\n")
    close(c)
}

func main() {
    wordChannel := make(chan string)
    go emit(wordChannel)
    for word := range wordChannel {
      fmt.Printf("receive %s \n", word)
    }
}

当我运行这个时,我得到了我认为是你想要的输出:

$ go run t.go
send The
send quick
receive The
receive quick
send brown
send fox
receive brown
receive fox
send ran
send away
receive ran
receive away
Close the channel

这个“range over a channel”语法在概念上等同于这样的东西,只是看起来更优雅一点:

func main() {
    wordChannel := make(chan string)
    go emit(wordChannel)
    for {
      if word, ok := <-wordChannel; ok {
        fmt.Printf("receive %s \n", word)
      } else {
        break
      }
  }
}