在一定时间内从频道阅读的惯用方式

Idiomatic way for reading from the channel for a certain time

我需要在一段时间内(比如 5 秒)从 Go 通道读取数据。带超时的 select 语句对我不起作用,因为我需要读取尽可能多的可用值并在 5 秒后准确停止。到目前为止,我想出了一个使用额外时间通道的解决方案 https://play.golang.org/p/yev9CcvzRIL

package main

import "time"
import "fmt"

func main() {
    // I have no control over dataChan
    dataChan := make(chan string)
    // this is a stub to demonstrate some data coming from dataChan
    go func() {
        for {
            dataChan <- "some data"
            time.Sleep(time.Second)
        }
    }()

    // the following is the code I'm asking about
    timeChan := time.NewTimer(time.Second * 5).C
    for {
        select {
        case d := <-dataChan:
            fmt.Println("Got:", d)
        case <-timeChan:
            fmt.Println("Time's up!")
            return
        }
    }
}

我想知道是否有更好或更惯用的方法来解决这个问题?

差不多就是这些了。但如果您不需要停止或重置计时器,只需使用 time.After() instead of time.NewTimer()time.After()"equivalent to NewTimer(d).C".

afterCh := time.After(5 * time.Second)
for {
    select {
    case d := <-dataChan:
        fmt.Println("Got:", d)
    case <-afterCh:
        fmt.Println("Time's up!")
        return
    }
}

或者(根据您的喜好),您可以在 for 语句中声明后通道,如下所示:

for afterCh := time.After(5 * time.Second); ; {
    select {
    case d := <-dataChan:
        fmt.Println("Got:", d)
    case <-afterCh:
        fmt.Println("Time's up!")
        return
    }
}

另外我知道这只是一个例子,但总是想着你启动的 goroutine 将如何正确结束,因为在你的情况下生成数据的 goroutine 永远不会终止。

另外不要忘记,如果可以不阻塞地执行多个案例,则随机选择一个。因此,如果 dataChan 准备好从 "non-stop" 接收,则无法保证循环会在超时后 立即 终止。在实践中,这通常不是问题(首先,即使超时也不能保证,请参阅 中的详细信息),但您不应在 "mission-critial" 应用程序中忘记它。详情见相关问题:

看起来带有截止日期的上下文很合适,比如

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

    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
    defer cancel()

    go func(ctx context.Context) {
        for {
            select {
            case dataChan <- "some data":
                time.Sleep(time.Second)
            case <-ctx.Done():
                fmt.Println(ctx.Err())
                close(dataChan)
                return
            }
        }
    }(ctx)

    for d := range dataChan {
        fmt.Println("Got:", d)
    }

}