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


相关推荐
神奇的程序员2 小时前
从已损坏的备份中拯救数据
运维·后端·前端工程化
oden3 小时前
AI服务商切换太麻烦?一个AI Gateway搞定监控、缓存和故障转移(成本降40%)
后端·openai·api
李慕婉学姐4 小时前
【开题答辩过程】以《基于Android的出租车运行监测系统设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·后端·vue
m0_740043734 小时前
SpringBoot05-配置文件-热加载/日志框架slf4j/接口文档工具Swagger/Knife4j
java·spring boot·后端·log4j
招风的黑耳5 小时前
我用SpringBoot撸了一个智慧水务监控平台
java·spring boot·后端
Miss_Chenzr5 小时前
Springboot优卖电商系统s7zmj(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
期待のcode5 小时前
Springboot核心构建插件
java·spring boot·后端
2501_921649495 小时前
如何获取美股实时行情:Python 量化交易指南
开发语言·后端·python·websocket·金融
serendipity_hky6 小时前
【SpringCloud | 第5篇】Seata分布式事务
分布式·后端·spring·spring cloud·seata·openfeign
五阿哥永琪6 小时前
Spring Boot 中自定义线程池的正确使用姿势:定义、注入与最佳实践
spring boot·后端·python