从频道阅读一段时间的惯用法

vit*_*itr 6 concurrency timeout channel idiomatic go

我需要从Go通道读取数据一段时间(比如说5秒).带超时的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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

icz*_*cza 12

这就是它.但如果您不需要停止或重置计时器,只需使用time.After()而不是time.NewTimer().time.After()"等同于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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

另外不要忘记,如果可以在不阻塞的情况下执行多个案例,则随机选择一个案例.因此,如果dataChan准备从"不间断"接收,则无法保证循环将在超时后立即终止.在实践中,这通常不是问题(从那开始即使超时也不是保证,看看Golang Timers的详细信息,长度为0),但你不应该忘记它在"任务 - 批评"应用程序中.有关详情,请参阅相关问题:

go select语句的强制优先级

golang:当涉及多个频道时,选择如何工作?

  • @vitr,如果 dataChan 不间断地提供值,则选择将在 5 秒后 [pseudo-random](https://golang.org/ref/spec#Select_statements) 并且可能无法可靠地命中 afterCh 情况。如果您不能容忍,请在尝试从 dataChan 接收之前明确检查 afterCh:https://play.golang.org/p/3_odFeXbWLf (2认同)