有没有办法在无限时间内询问time.After()?
有没有办法要求time.After()无限量的时间?
动机:我有一个服务,调用者可以从它请求消息,并有一个可选的超时。这样做的明显方法是:
func service(timeout *time.Duration) SomeType {
var timeout_value time.Duration
if timeout != nil {
timeout_value = *timeout
} else {
timeout_value = time.Forever /* or something */
}
select {
case value <- some_channel:
return value
case <- time.After(timeout_value):
return nil
}
}
除了我不知道有没有办法说time.Forever。
回答
没有“永远”持续时间,但有最大持续时间:
const maxDuration time.Duration = 1<<63 - 1
maxDuration大约是 292 年。它应该足以满足单个应用程序的生命周期。但相反,我提出了以下不使用它的解决方案:
请注意,如果“永远”是预期的最大等待时间,则省略time.After()和使用简单的接收会更简单、更有效:
func service(timeout *time.Duration) SomeType {
if timeout == nil {
return <-some_channel
}
select {
case value := <-some_channel:
return value
case <-time.After(*timeout):
return nil
}
}
您表示您的实际代码要复杂得多并且包含更多案例。
在这种情况下,我会将超时通道创建移到select语句之外,并相应地进行初始化。当timeoutis 时nil,只离开通道nil(它的零值),它永远不会提供任何值,因此从nil通道接收实际上需要“永远”:
func service(timeout *time.Duration) SomeType {
var timeoutCh <-chan time.Time
if timeout != nil {
timeoutCh = time.After(*timeout)
}
select {
case value := <-some_channel:
return value
case <-timeoutCh:
return nil
}
}
- @Flimzy 是的,我担心,即使我提出的解决方案甚至不使用`maxDuration`,我相信他会回到这里并拒绝这个答案:)
- 我预测在 2313 年,当某些应用程序的唯一显示单元停止工作时,某些博物馆馆长会诅咒您。
- @Tom 好吧,请参阅我编辑过的答案。简而言之:只需将超时通道创建移到`select`之外,如果你想永远等待,让它保持`nil`。从“nil”频道接收永远阻塞。
回答
您可以context.Context在函数中接受 a 而不是持续时间,我认为这在 Go 代码中非常惯用。
然后调用者可以根据需要使用 aContext.Background或 a调用该函数Context.WithTimeout。该service函数选择上下文的Done(),在背景上下文的情况下永远不会结束(chan 实际上为零)。
如果此上下文永远无法取消,则 Done 可能返回 nil。[...] Done 用于选择语句
func callerNoTimeout() {
foo := service(context.Background())
}
func callerTimeout() {
foo := service(context.WithTimeout(context.Background(), timeOut))
}
func service(ctx context.Context) SomeType {
select {
case value <-some_channel:
return value
case <-ctx.Done():
return nil
}
}