Go语言实战案例:任务调度器:定时执行任务

在后端服务、日志采集、数据清洗、健康检查等应用中,我们经常需要实现"每隔一段时间执行某个任务"。Go 标准库 time 包提供了非常强大且简洁的支持,配合协程可轻松构建定时任务调度器。


一、需求背景

我们希望实现一个任务调度器,能够:

  • • 每隔固定时间执行某个函数;
  • • 支持并发安全;
  • • 可停止调度器(支持任务终止);
  • • 可扩展多个任务调度。

二、实现方案一:使用 time.Ticker

Go 的 time.Ticker 提供了周期性触发的功能。配合 selectgoroutine 可实现高效的调度器。

示例:每 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 执行数据备份

五、注意事项

    1. 资源释放
      使用 time.Ticker 时,一定要在停止时调用 ticker.Stop(),否则会泄露定时器资源。
    1. 线程安全
      如果调度器涉及状态管理(如是否运行中),需要使用 sync.Mutex 加锁保护。
    1. 协程退出机制
      使用 chan struct{} 来安全地关闭 goroutine 是一种推荐方式。

六、进阶建议

  • • 可结合 context.Context 实现更细粒度的取消控制;
  • • 支持动态更改调度时间(重设 ticker);
  • • 结合 cron 表达式(第三方库如 robfig/cron)实现更复杂的计划任务;
  • • 支持任务失败重试与日志记录机制。

七、结语

通过 time.Ticker 和 goroutine,Go 可以非常优雅地实现定时任务调度器。虽然标准库方案简单,但足以应对大多数日常应用。你可以在此基础上封装出强大的任务调度框架,用于后台服务、数据处理、定时推送等业务场景。


相关推荐
bobz9653 小时前
5070 Ti CodeLlama 7B > Mistral 7B > Qwen3 8B
后端
前端日常开发4 小时前
我用Trae写了个数字滚动器,结果看了一下午!
trae
麦兜*4 小时前
Spring Boot 集成 Docker 构建与发版完整指南
java·spring boot·后端·spring·docker·系统架构·springcloud
程序视点4 小时前
2025最佳图片无损放大工具推荐:realesrgan-gui评测与下载指南
前端·后端
fured5 小时前
[调试][实现][原理]用Golang实现建议断点调试器
开发语言·后端·golang
围巾哥萧尘6 小时前
烟花模拟器 Fireworks Simulator 🧣
trae
bobz9656 小时前
linux cpu CFS 调度器有使用 令牌桶么?
后端
bobz9656 小时前
linux CGROUP CPU 限制有使用令牌桶么?
后端
David爱编程7 小时前
多核 CPU 下的缓存一致性问题:隐藏的性能陷阱与解决方案
java·后端
围巾哥萧尘7 小时前
AI智能旅游规划 让每次旅行都完美无缺🧣
trae