Exploring Time Control in Go: A Comprehensive Guide
Edison failed 10,000 times before he made the electric light. Do not be discouraged if you fail a few times.
— Napoleon Hill
Medium Link: Time Control You Should Know in Golang | by Wesley Wei | Jun, 2024 | Programmer’s Career
Hello, here is Wesley, Today’s article is about time control in Go. Without further ado, let’s get started.💪
Introduction of the Problem
Recently, I encountered a requirement where my program needs to schedule some tasks periodically, requiring fine-grained control over time. Let’s start with the program’s component architecture diagram:
The specific requirement is that the manager needs to periodically trigger task allocation, and the worker also needs to periodically fetch tasks from the database and execute them. Additionally, it is crucial that the execution time for both manager and worker is fixed.
For example, let’s consider the manager executing tasks for 20 minutes and the worker executing tasks for 60 minutes from a time perspective:
It is clear that both manager and worker require periodic execution of tasks. In my career using Go, I have encountered many scenarios related to time control. This seems like a good opportunity to summarize and share my experiences.
So, how should we control time in Go? What are some use cases? How should I implement this specific requirement?
Time Control
In the process of summarizing, I found that time control can be roughly divided into two types of scenarios:
- Timeout and resource recovery scenarios (channel+context, channel + time.After/time.NewTimer)
- Periodic scenarios (channel + time.After/time.NewTimer, cron package)
Different scenarios require control of different aspects, let’s continue to look below.
channel + context
1 | func main() { |
The main logic is to use context for time control. The main goroutine sets an 800 millisecond timeout, if the sub-goroutine does not complete execution, we wait at most 800 milliseconds. If the timeout is exceeded, we can consider retrying or recycling resources. If the sub-goroutine makes a successful request, we can continue the main goroutine’s business logic.
It can be seen that the focus of time control in this scenario is on request timeout and resource recovery.
PS: For more in-depth content on context, you can refer to my other article:
channel + time.After/time.NewTimer
1 | func main() { |
Using time.After
can also be used for timeout control. However, every time time.After
is called, a new timer and corresponding channel are created, and there is no good resource recovery method. I do not recommend this usage.
To avoid potential memory leaks, a common practice is to use time.NewTimer
instead of time.After
, as shown below:
1 | func main() { |
Compared to context, I think context is a more elegant method because it can be combined with context’s features to make finer control (such as the main goroutine canceling, and the sub-goroutine also canceling). However, the time channel method is a simpler approach, and the two have different applications in timeout control scenarios.
Of course, the time channel method can also be used for periodic work.
1 | // Define a task, use an empty struct as a simple example here |
Note: In actual code, you might need a better way to stop the timer and exit the program. The
for
loop in this example will keep running until the program is forcefully terminated.
Also, the task in this example is an empty struct. You will need to define a task structure that fits your business requirements in real-world cases.
This example can be used to observe the normal operation of your service. It will periodically check the length of the task channel, if the length is zero for a long time, then our tasks are likely to have occurred an exception.
cron package
Periodically observing the length of the task channel is a lightweight task, but what about more complex scenarios? The answer is the cron package.
Callers may register Funcs to be invoked on a given schedule. Cron will run them in their own goroutines.
1 | c := cron.New() |
For more information, refer to: cron package - github.com/robfig/cron - Go Packages
Real-world application
My requirement can obviously be implemented using the cron package.
After thinking about it, I made the above implementation. The manager triggers a task allocation once when the process starts, and subsequently triggers it every hour. This way, the worker can make better use of time to execute more tasks.
Summary
Whether it’s the time pacakage time or the cron pacakage, they both rely on the power of channels. I am grateful for the design of golang context and channels, which makes developing time control-related code easier.
More
While summarizing the article, I thought of the following questions:
- Why is there a need for periodic triggering of tasks?
- Periodicity is to prevent the accumulation of self-tasks and to dynamically do more tasks within each period. Within each period, we try to do as many tasks as possible based on the maximum capacity of the downstream interface.
- In the Go 1.23 Release Notes, it is mentioned that “Timer’s and Ticker’s that are no longer referred to by the program become eligible for garbage collection immediately.” How is this achieved? Why wasn’t it immediately recycled before?
- In a select statement, when messages from <-done and <-ctx.Done() occur simultaneously, which one will be chosen? Why?
1 | select { |
Regarding the second and third points, feel free to share your thoughts.
References
cron package - github.com/robfig/cron - Go Packages
More Series Articles about You Should Know In Golang:
https://wesley-wei.medium.com/list/you-should-know-in-golang-e9491363cd9a
And I’m Wesley, delighted to share knowledge from the world of programming.
Don’t forget to follow me for more informative content, or feel free to share this with others who may also find it beneficial. it would be a great help to me.
See you in the next article. 👋
中文文章: https://programmerscareer.com/zh-cn/golang-time-control/
Author: Wesley Wei – Twitter Wesley Wei – Medium
Note: Originally written at https://programmerscareer.com/golang-time-control/ at 2024-06-23 18:26. If you choose to repost or use this article, please cite the original source.
Comments