在后端服务、日志采集、数据清洗、健康检查等应用中,我们经常需要实现"每隔一段时间执行某个任务"。Go 标准库
time
包提供了非常强大且简洁的支持,配合协程可轻松构建定时任务调度器。
一、需求背景
我们希望实现一个任务调度器,能够:
- • 每隔固定时间执行某个函数;
- • 支持并发安全;
- • 可停止调度器(支持任务终止);
- • 可扩展多个任务调度。
二、实现方案一:使用 time.Ticker
Go 的 time.Ticker
提供了周期性触发的功能。配合 select
和 goroutine
可实现高效的调度器。
示例:每 2 秒执行一次任务
go
package main
import (
"fmt"
"time"
)
func task() {
fmt.Println("任务执行时间:", time.Now())
}
func main() {
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
done := make(chan bool)
go func() {
for {
select {
case <-done:
fmt.Println("调度器已停止")
return
case t := <-ticker.C:
fmt.Println("调度器触发:", t)
task()
}
}
}()
// 模拟运行一段时间后终止
time.Sleep(10 * time.Second)
done <- true
}
输出示例:
erlang
调度器触发:2025-08-05 20:00:00
任务执行时间:2025-08-05 20:00:00
调度器触发:2025-08-05 20:00:02
任务执行时间:2025-08-05 20:00:02
...
调度器已停止
三、实现方案二:封装通用任务调度器结构体
为了更灵活地管理多个任务,我们可以封装一个 Scheduler
类型。
go
type TaskFunc func()
type Scheduler struct {
ticker *time.Ticker
quit chan struct{}
running bool
mu sync.Mutex
}
func NewScheduler(interval time.Duration, task TaskFunc) *Scheduler {
return &Scheduler{
ticker: time.NewTicker(interval),
quit: make(chan struct{}),
running: false,
}
}
func (s *Scheduler) Start(task TaskFunc) {
s.mu.Lock()
if s.running {
s.mu.Unlock()
return
}
s.running = true
s.mu.Unlock()
go func() {
for {
select {
case <-s.quit:
return
case <-s.ticker.C:
task()
}
}
}()
}
func (s *Scheduler) Stop() {
s.mu.Lock()
defer s.mu.Unlock()
if s.running {
close(s.quit)
s.ticker.Stop()
s.running = false
}
}
使用示例:
css
func main() {
scheduler := NewScheduler(3*time.Second, nil)
scheduler.Start(func() {
fmt.Println("执行任务:", time.Now())
})
time.Sleep(10 * time.Second)
scheduler.Stop()
fmt.Println("调度器停止")
}
四、任务调度器使用场景举例
场景 | 调度内容 |
---|---|
日志清理 | 每天凌晨清除旧日志 |
服务心跳 | 每 5 秒上报健康状态 |
数据同步 | 每小时同步远程数据 |
自动备份 | 每天 23:00 执行数据备份 |
五、注意事项
-
- 资源释放 :
使用time.Ticker
时,一定要在停止时调用ticker.Stop()
,否则会泄露定时器资源。
- 资源释放 :
-
- 线程安全 :
如果调度器涉及状态管理(如是否运行中),需要使用sync.Mutex
加锁保护。
- 线程安全 :
-
- 协程退出机制 :
使用chan struct{}
来安全地关闭 goroutine 是一种推荐方式。
- 协程退出机制 :
六、进阶建议
- • 可结合
context.Context
实现更细粒度的取消控制; - • 支持动态更改调度时间(重设 ticker);
- • 结合
cron
表达式(第三方库如robfig/cron
)实现更复杂的计划任务; - • 支持任务失败重试与日志记录机制。
七、结语
通过 time.Ticker
和 goroutine,Go 可以非常优雅地实现定时任务调度器。虽然标准库方案简单,但足以应对大多数日常应用。你可以在此基础上封装出强大的任务调度框架,用于后台服务、数据处理、定时推送等业务场景。