【问题标题】:Is there a way to ask time.After() for an infinite amount of time?有没有办法询问 time.After() 无限的时间?
【发布时间】:2021-08-12 12:53:17
【问题描述】:

有没有办法无限期地向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

【问题讨论】:

    标签: go time


    【解决方案1】:

    没有“永远”的持续时间,但有最长持续时间:

    const maxDuration time.Duration = 1<<63 - 1
    

    maxDuration 大约是 292 年。对于单个应用程序的生命周期来说,这应该足够了。但相反,我提出了以下不使用它的解决方案:

    请注意,如果“forever”是预期的最大等待时间,则省略 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 语句之外,并相应地进行初始化。当timeoutnil 时,只需离开频道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
        }
    }
    

    【讨论】:

    • 我预测在 2313 年,当某些应用程序的唯一显示单元停止工作时,一些博物馆馆长会诅咒你。
    • @Flimzy 是的,我担心这一点,我相信他会回到这里并否决这个答案,即使我提出的解决方案甚至没有使用 maxDuration :)
    • 为什么不1&lt;&lt;64 - 1
    • @super:因为它是 int64,而不是 uint64。
    • @Tom 好吧,看看我编辑的答案。简而言之:只需将超时通道创建移到select 之外,如果您想永远等待,让它保持nil。从nil 频道接收永远阻塞。
    【解决方案2】:

    您可以在函数中接受 context.Context 而不是持续时间,我认为这在 Go 代码中是非常惯用的。

    然后调用者可以根据需要使用Context.BackgroundContext.WithTimeout 调用该函数。 service 函数在上下文的 Done() 上进行选择,如果后台上下文永远不会结束(chan 实际上为零)。

    如果这个上下文永远不能被取消,Done 可能会返回 nil。 [...] Done 供在 select 语句中使用

    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
        }
    }
    

    【讨论】:

      【解决方案3】:

      首先,通常的做法是使用 time.Duration0(或负数)来表示没有超时 - 因此没有必要传递指针。

      其次,在是否强制超时时检查这个零值:

      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
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-11-22
        • 2012-11-15
        • 2014-11-14
        • 2016-01-06
        • 1970-01-01
        • 1970-01-01
        • 2015-08-11
        相关资源
        最近更新 更多