在一定时间内从频道阅读的惯用方式
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)
}
}
我需要在一段时间内(比如 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" 接收,则无法保证循环会在超时后 立即 终止。在实践中,这通常不是问题(首先,即使超时也不能保证,请参阅
看起来带有截止日期的上下文很合适,比如
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)
}
}