有没有办法无限期地询问 time.After() ?
Is there a way to ask time.After() for an infinite amount of time?
有没有办法无限期地询问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
语句之外,并进行相应的初始化。当 timeout
为 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
}
}
首先,通常的做法是使用 0
的 time.Duration
(或负数)来指示没有超时 - 因此没有必要传递指针。
其次,在是否强制超时时检查这个零值:
func service(timeout time.Duration) SomeType {
if timeout <= 0 {
return <- some_channel
}
select {
case value <- some_channel:
return value
case <- time.After(timeout):
return nil
}
}
你可以在你的函数中接受 context.Context
而不是持续时间,我认为这在 Go 代码中是非常惯用的。
然后调用者可以根据需要用Context.Background
or with a Context.WithTimeout
调用函数。 service
函数选择上下文的 Done()
,在背景上下文永远不会结束的情况下(chan 实际上为 nil)。
Done may return nil if this context can never be canceled. [...] Done is provided for use in select statements
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
}
}
有没有办法无限期地询问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
语句之外,并进行相应的初始化。当 timeout
为 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
}
}
首先,通常的做法是使用 0
的 time.Duration
(或负数)来指示没有超时 - 因此没有必要传递指针。
其次,在是否强制超时时检查这个零值:
func service(timeout time.Duration) SomeType {
if timeout <= 0 {
return <- some_channel
}
select {
case value <- some_channel:
return value
case <- time.After(timeout):
return nil
}
}
你可以在你的函数中接受 context.Context
而不是持续时间,我认为这在 Go 代码中是非常惯用的。
然后调用者可以根据需要用Context.Background
or with a Context.WithTimeout
调用函数。 service
函数选择上下文的 Done()
,在背景上下文永远不会结束的情况下(chan 实际上为 nil)。
Done may return nil if this context can never be canceled. [...] Done is provided for use in select statements
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
}
}