gcron 定时任务:Add 与 AddSingleton 用法与注意事项
做后台系统时,我们经常需要“到点干活”:每天凌晨跑对账、每 5 分钟同步一次缓存。GoFrame v2 的 gcron 组件(github.com/gogf/gf/v2/os/gcron)提供类似 crontab 的定时任务能力,最小粒度到“秒”,是官方文档重点介绍的调度组件。
基本概念
pattern:调度表达式,六段式 CRON 语法(秒 分 时 日 月 周),也支持@hourly、@every 1h30m别名。- 任务函数:
func(ctx context.Context),签名固定,ctx由调度器注入。 - 任务名
name:可选但要求唯一;重名添加会失败并返回错误。 - 默认使用进程全局时区计算执行时间,
Add之后任务自动启动。
使用 Add 添加普通任务
package main
import (
"context"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcron"
"github.com/gogf/gf/v2/os/gctx"
"time"
)
func main() {
var ctx = gctx.New()
// 每秒执行一次,并指定唯一任务名;@hourly、@every 1h30m 等别名同样可用
_, err := gcron.Add(ctx, "* * * * * *", func(ctx context.Context) {
g.Log().Info(ctx, "every second")
}, "job-every-second")
if err != nil {
panic(err) // 任务名重复等错误会在这里返回
}
time.Sleep(3 * time.Second)
}
通过 gcron.Entries() 可查看全部已注册任务(含名称与下次执行时间);gcron.New() 可创建独立的管理对象,把互不相关的任务组隔离。
使用 AddSingleton 添加单例任务
单例任务保证“同一时刻最多只有一个该任务在运行”:上一个任务还没结束,下一次触发会被跳过,而不是并发再起协程。任务耗时较长、不要求严格按点执行时(数据同步、报表生成)应优先使用单例:
func main() {
var ctx = gctx.New()
// 任务每次耗时 2 秒,但调度周期是 1 秒
_, err := gcron.AddSingleton(ctx, "* * * * * *", func(ctx context.Context) {
g.Log().Info(ctx, "doing sync job")
time.Sleep(2 * time.Second)
}, "job-sync")
if err != nil {
panic(err)
}
select {}
}
观察日志可以发现输出大约每 2 秒一次而非每 1 秒一次——重叠触发被丢弃,这正是单例效果。官方文档明确:去重判断发生在进程内存内,因此只保证“单进程单例”。
任务管理方法
cron := gcron.New() // 自建管理对象,便于隔离多组任务
_, _ = cron.Add(ctx, "@every 2s", job, "job-a")
cron.Stop("job-a") // 暂停:任务保留但不再触发
cron.Start("job-a") // 恢复执行
cron.Remove("job-a") // 停止并删除任务
entry := cron.Search("job-a") // 按名查找,返回 *Entry 或 nil
AddOnce 添加只执行一次的任务,AddTimes 添加执行指定次数的任务,两者跑完后自动销毁;v2.8 起提供 StopGracefully,会等待正在执行的任务结束后再暂停,适合优雅退出流程。
注意事项
- 进程全局时区直接影响调度,部署前先确认服务器/容器时区(时区设置见官方 gtime 章节)。
- 任务名必须唯一,重名添加会失败。
Stop只是暂停并立即返回,不会等待正在执行的任务结束。- 单例去重仅限单进程;多实例部署需自行加分布式锁(如 Redis)防重复执行。
- 回调内的错误请自己记录日志,避免任务“静默失败”。
小结
gcron.Add(普通任务)与 AddSingleton(单例任务)覆盖绝大多数定时场景:表达式决定“什么时候跑”,单例模式决定“别挤在一起跑”,Stop/Start/Remove 负责运行期管理。更多方法细节以官方文档 goframe.org 为准。