Go > Concurrency > Concurrency Patterns > Ticker and Timer
Go Ticker and Timer Example
Demonstrates the use of time.Ticker
and time.Timer
in Go for scheduling tasks at regular intervals and after a specific delay.
Introduction to Tickers and Timers
In Go, time.Ticker
and time.Timer
are essential tools for managing time-based events in concurrent programs. A time.Ticker
triggers events at regular intervals, while a time.Timer
triggers an event once after a specified duration. They are widely used for tasks like periodic updates, timeouts, and scheduling operations.
Ticker Example: Periodic Task Execution
This example creates a time.Ticker
that sends a value on its channel C
every second. The select
statement waits for either a tick from the ticker or a timeout. The ticker prints a message each time it ticks, and the program exits after 5 seconds. The defer ticker.Stop()
call ensures that the ticker is stopped when the program finishes, preventing resource leaks.
package main
import (
"fmt"
"time"
)
func main() {
// Create a new ticker that ticks every 1 second
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop() // Ensure the ticker is stopped when the function exits
// Run for 5 seconds
timeout := time.After(5 * time.Second)
for {
select {
case <-ticker.C:
fmt.Println("Tick at", time.Now().Format(time.RFC3339))
case <-timeout:
fmt.Println("Timeout! Exiting...")
return
}
}
}
Explanation of the Code
time.NewTicker(1 * time.Second)
: Creates a new ticker that will send the current time on its channel every second.defer ticker.Stop()
: Stops the ticker when the main function returns. This is important to prevent the ticker from continuing to send events indefinitely.time.After(5 * time.Second)
: Creates a channel that will receive a value after 5 seconds.select
: Waits for either a tick from the ticker's channel (ticker.C
) or a signal from the timeout channel.fmt.Println("Tick at", time.Now().Format(time.RFC3339))
: Prints a message each time the ticker ticks.fmt.Println("Timeout! Exiting...")
: Prints a message when the timeout occurs and then exits the program.
Timer Example: Delayed Task Execution
This example creates a time.Timer
that will send a value on its channel C
after 3 seconds. The program blocks until the timer fires, then prints a message. defer timer.Stop()
will stop the timer if it has not already fired, preventing a potential resource leak. This is important if the surrounding logic can exit before the timer fires.
package main
import (
"fmt"
"time"
)
func main() {
// Create a new timer that will fire after 3 seconds
timer := time.NewTimer(3 * time.Second)
defer timer.Stop() // Ensure the timer is stopped if it's no longer needed
fmt.Println("Waiting for timer...")
<-timer.C // Block until the timer fires
fmt.Println("Timer fired at", time.Now().Format(time.RFC3339))
}
Explanation of the Code
time.NewTimer(3 * time.Second)
: Creates a new timer that will send the current time on its channel after 3 seconds.defer timer.Stop()
: Stops the timer when the main function returns, preventing the timer from potentially sending an event after the program has finished its primary task. This is especially important if you're using timers in functions that might return early under certain conditions.<-timer.C
: Blocks until the timer's channel receives a value, signaling that the timer has fired.fmt.Println("Timer fired at", time.Now().Format(time.RFC3339))
: Prints a message indicating when the timer fired.
Concepts Behind the Snippets
time.Ticker
and time.Timer
are built upon Go's concurrency model using channels. They provide a way to send signals across goroutines at specific times or intervals. The Stop()
method is crucial for preventing resource leaks, especially when the ticker or timer might not fire before the program exits or the surrounding function returns. Always remember to handle the potential for timers or tickers that are no longer needed.
Real-Life Use Case Section
Tickers: Polling a database for changes at regular intervals, updating a UI with live data every few seconds, sending heartbeat signals to a server. Timers: Implementing timeouts for network requests, retrying an operation after a delay, scheduling a cleanup task to run after a certain period of inactivity.
Best Practices
Stop()
on tickers and timers when they are no longer needed to prevent resource leaks.select
statements to handle timeouts and avoid blocking indefinitely.
Interview Tip
Be prepared to explain the difference between time.Ticker
and time.Timer
, and how they can be used in concurrent programs. Also, be ready to discuss the importance of stopping tickers and timers to prevent resource leaks and the use of select
for handling timeouts.
When to use them
Ticker: Use when you need to perform an action repeatedly at regular intervals. Timer: Use when you need to perform an action once after a specified delay.
Memory footprint
Both time.Ticker
and time.Timer
consume resources, including a goroutine and a channel. Stopping them releases these resources. Not stopping them can lead to memory leaks, especially in long-running applications.
Alternatives
Instead of using Tickers directly, consider using libraries that abstract away the complexities of managing goroutines and channels for scheduled tasks. For simpler scenarios, you could also implement your own polling mechanisms with time.Sleep
, but this is generally less efficient and harder to manage for complex scheduling requirements.
Pros
Cons
FAQ
-
What happens if I don't call
Stop()
on a ticker?
The ticker will continue to send events on its channel, even after you no longer need them. This can lead to resource leaks and unexpected behavior. -
Can I reset a timer?
Yes, you can reset a timer using theReset()
method. However, be careful when resetting timers, as it can lead to race conditions if not done properly. -
Are tickers and timers precise?
Tickers and timers are not guaranteed to be perfectly precise. The actual time at which an event is triggered may vary slightly due to system load and other factors.