Go channel 不是 receiving/printing 发送到 channel 的最后一个值

Go channel not receiving/printing last value sent to channel

这是一段代码,它输出推送到频道的整数列表。否则 select 检查并打印必要的超时消息。

package main

import (
    "fmt"
    "sync"
    "time"
)

var wg sync.WaitGroup

func main() {
    wg.Add(1)
    c := make(chan int)

    go readFromChannel(c, time.After(time.Duration(2)*time.Second))
//  time.Sleep(time.Duration(5) * time.Second) //Talking about uncommenting this line
    c <- 10
    c <- 20
    c <- 30
    c <- 40
    c <- 50
    wg.Wait()
}

func readFromChannel(c chan int, ti <-chan time.Time) {
    defer wg.Done()
    go func() {
        for {
            select {
            case x := <-c:
                fmt.Println("Read", x)
            case t :=<-ti:
                fmt.Println("TIMED OUT with "+t.String())
            }
        }
    }()

}

这里还有游乐场 link 同样的:https://play.golang.org/p/4hNWze4Pfwr 此代码输出整数列表,如

Read 10
Read 20
Read 30
Read 40
Read 50

但是当我取消注释使主例程进入睡眠 5 秒的行时(在代码中提到为 comment/line 数字 16),输出更改为:

TIMED OUT with 2009-11-10 23:00:02 +0000 UTC m=+2.000000001
Read 10
Read 20
Read 30
Read 40

我想了解为什么最后一个 Read 50 没有在第二种情况下打印出来。

问题是你的wg.Done()放错了地方。它必须在你的 goroutine 中,但是你在 goroutine 启动之前就已经执行了它,所以你的程序很可能在做任何工作之前退出。

改变这个:

defer wg.Done()
go func() {

对此:

go func() {
    defer wg.Done()

当然你会有一个无限 运行 goroutine,因为你的 for 循环没有退出条件。您需要添加一个,可能是通过检查频道是否关闭:

        select {
        case x, ok := <-c:
            if !ok { // channel was closed
                return
            }
            fmt.Println("Read", x)
        case t :=<-ti:
            fmt.Println("TIMED OUT with "+t.String())
        }

然后告诉您的主要 go 例程在完成后关闭通道:

c <- 40
c <- 50
close(c)  // We're done, so tell the goroutine to finish up
wg.Wait() // But wait until it's done