问题
I have some code, there are 3 timers,
- GracefulExecutionTimeout - total running time
- WaitTimeout - time to initial wait for first message
- IdleTimeout - time to wait for subsequent messages
If any of the timers are reached the app should cleanly exit. I have it working below
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 ...
回答1:
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 (for details, see How does a non initialized channel behave?).
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.
来源:https://stackoverflow.com/questions/61408256/conditional-timeout