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 可以非常优雅地实现定时任务调度器。虽然标准库方案简单,但足以应对大多数日常应用。你可以在此基础上封装出强大的任务调度框架,用于后台服务、数据处理、定时推送等业务场景。


相关推荐
码事漫谈6 分钟前
你的代码可能在偷偷崩溃!
后端
dylan_QAQ13 分钟前
【附录】Spring容器的启动过程是怎样的?
后端·spring
白应穷奇16 分钟前
编写高性能数据处理代码 02
后端·python
这个做不了18 分钟前
基于模板方法与工厂模式的多支付公司/产品接入方案
后端
Moonbit34 分钟前
MoonBit Pearls Vol.03:01背包问题
后端·算法·编程语言
南囝coding1 小时前
这个仓库堪称造轮子的鼻祖,建议看看!
前端·后端
JuiceFS1 小时前
3000 台 JuiceFS Windows 客户端性能评估
后端·云原生·云计算
Cosolar1 小时前
下一代 Python Web 框架?FastAPI 全面解析与实战对比
后端·python
用户4822137167752 小时前
C++——复合数据类型(数组、字符串)
后端
用户903700167152 小时前
分布式阻塞式限流学习及分享
后端