Create A Golang Timer Using A Goroutine
One of our engineering teams needed to monitor long-running jobs in some of our Golang code. If the job ran for more than 60 seconds, we would be notified. Our solution was to create a timer utility, using Go’s time package and a goroutine.
Timer Pattern
First, the basic pattern for creating a timer:
package mainimport (
"time"
"fmt"
)const time_in_seconds = 60func main() {
// 60 second timer.
timer := time.NewTimer(time.Second*time_in_seconds)
// Stop the timer at the end of the function.
// Defers are called when the parent function exits.
defer timer.Stop()
// Wait for timer to finish in an asynchronous goroutine
go func() {
// Block until timer finishes. When it is done, it sends a message
// on the channel timer.C. No other code in
// this goroutine is executed until that happens.
<-timer.C
// If main() finishes before the 60 second timer, we won't get here
fmt.Printf("Congratulations! Your %d second timer finished.", time_in_seconds)
}
executeTheCodeThatMightTakeMoreThanSixtySeconds()
// The main function is finished, so the timer will be told to stop.
}
Extract Timer Utility Function
We extracted the above pattern into a utility function. It runs a timer for a specified period of time, and then calls a supplied function.
package mainimport (
"time"
"fmt"
)const time_in_seconds = 60func main() { // Run timer for 60 seconds. When timer expires,
// call a function to print out that our timer is done.
timer := NewTimer(time_in_seconds, func() {
fmt.Printf("Congratulations! Your %d second timer finished.", time_in_seconds)
})
defer timer.Stop()
executeTheCodeThatMightTakeMoreThanSixtySeconds()
}// NewTimer creates a timer that runs for a specified number of seconds.
// When the timer finishes, it calls the action function.
// Use the returned timer to stop the timer early, if needed.
func NewTimer(seconds int, action func()) *time.Timer {
timer := timer.NewTimer(time.Seconds * time.Duration(seconds))
go func() {
<-timer.C
action()
}
return timer
}
time.AfterFunc
We eventually discovered time
already has a utility function, AfterFunc
, that accomplishes what we did in our own utility. Fool of a Took!
timer := time.AfterFunc(time.Second*60, func() {
fmt.Printf("Congratulations! Your %d second timer finished.", time_in_seconds)
})
defer timer.Stop()
I’ve created a working example on the Go playground: https://play.golang.org/p/wXI-U5XgnG.
Summary
Use the standard time.AfterFunc
utility – not ours – But I hope these examples shed light on goroutines, channels, and defers. They are some of the coolest features in Go.