条件超时

我有一些代码,有3个计时器,

  • GracefulExecutionTimeout-总运行时间
  • WaitTimeout-初始等待第一条消息的时间
  • IdleTimeout-等待后续消息的时间

如果达到任何计时器,则应用程序应干净退出。我在下面工作

msgs := make(chan string)

go func() {
    time.Sleep(time.Second)
    msgs <- "test"
}()
// graceful max execution time
gracefulMaxTimeout := time.Second * time.Duration(10)
gracefulMaxTimer := time.NewTimer(gracefulMaxTimeout)

// idleTimeout
idleTimeout := time.Second * time.Duration(5)
idleTimer := time.NewTimer(idleTimeout)

// waitTimeout
waitTimeout := time.Second * time.Duration(2)
waitTimer := time.NewTimer(waitTimeout)
for {
    select {
    case <-gracefulMaxTimer.C:
        fmt.Println("GracefulMaxExecutionTimeout Reached")

        // graceful exit
        os.Exit(0)
    case <-idleTimer.C:
        fmt.Println("IdleTimeout Reached")

        // graceful exit
        os.Exit(0)
    case <-waitTimer.C:
        fmt.Println("WaitTimeout Reached")
        // graceful exit
        os.Exit(0)
    case msg := <-msgs:
        // stop wait timer
        waitTimer.Stop()
        fmt.Println(msg)

        // Reset idle timer
        if !idleTimer.Stop() {
            <-idleTimer.C
        }
        fmt.Println("IdleIimeout Reset")
        idleTimer.Reset(idleTimeout)
    }
}

Go Playground

I want to make the WaitTimeout optional but not sure how to approach it. If i surround the construction of the waitTimer with an if statement then it wont work as the waitTimer isnt defined for the select statement ... How can i make the WaitTimeout conditional ?

I can just .Stop() the timer after its created but that seems a little dirty ...

评论
  • taut
    taut 回复

    You may declare the wait timer and its channel outside the if statement, and only initialize them if wait timer is needed. If not, the channel may remain its zero value–which is nil–because receiving from a nil channel blocks forever, so this case will never be ready.

    useWaitTimer := true
    
    var (
        waitTimer  *time.Timer
        waitTimerC <-chan time.Time
    )
    if useWaitTimer {
        waitTimeout := time.Millisecond * time.Duration(500)
        waitTimer = time.NewTimer(waitTimeout)
        waitTimerC = waitTimer.C
    }
    
    // ...
    
    for {
        select {
        // ...
    
        case <-waitTimerC:
            fmt.Println("WaitTimeout Reached")
            // graceful exit
            os.Exit(0)
    
        // ...
        }
    }
    

    Then of course you can only reset the wait timer if it exists, this must also be checked (and don't forget to drain the channel if it returns false):

    // stop wait timer if exists
    if waitTimer != nil && !waitTimer.Stop() {
        <-waitTimerC
    }
    

    Try it on the Go Playground.