
本文详解 go 中使用 robfig/cron 库实现定时任务的正确姿势,重点解决程序启动后立即退出、cron 表达式误用及进程常驻问题,并提供可运行的完整示例。
在 Go 中使用 github.com/robfig/cron(v2 及以下版本,注意:v3+ 已更名为 github.com/robfig/cron/v3 且 API 有较大变化)实现定时任务时,一个常见误区是:调用 c.Start() 后未保持主 goroutine 活跃,导致程序瞬间退出,定时器根本来不及触发。此外,原始代码中使用的 cron 表达式 "1 * * * * *" 实际表示「每分钟的第 1 秒触发」(即每 60 秒一次),而非「每秒触发」,这源于对六字段 cron 格式的误解。
要实现真正的「每秒执行」并让程序长期运行,需同时满足两个条件:
- 使用正确的 cron 表达式:六字段格式为 ,因此 * * * * * * 表示「每秒执行一次」;
- 防止 main 函数提前退出:必须阻塞主线程,等待外部信号(如 Ctrl+C)终止程序。
以下是修正后的完整可运行示例(兼容 cron v2):
package main
import (
"fmt"
"os"
"os/signal"
"time"
"github.com/robfig/cron"
)
func main() {
c := cron.New()
// ✅ 正确:每秒执行一次(六字段,首字段为秒)
c.AddFunc("* * * * * *", RunEverySecond)
// ✅ 必须:在新 goroutine 中启动 cron,避免阻塞 main
go c.Start()
// ✅ 阻塞 main:监听 OS 中断信号(Ctrl+C 或 kill)
sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, os.Kill)
<-sig // 等待信号,程序在此挂起
fmt.Println("Shutting down...")
c.Stop() // 可选:优雅停止 cron 调度器
}
func RunEverySecond() {
fmt.Printf("[%s] Task executed\n", time.Now().Format("15:04:05"))
}? 关键注意事项:
- 若使用 cron/v3(推荐新项目采用),需改用 cron.New() → cron.New(cron.WithSeconds()),且表达式仍为 * * * * * *;
- c.Start() 是非阻塞方法,必须显式启动 goroutine(go c.Start()),否则后续 signal.Notify 将无法执行;
- 不要使用 time.Sleep(1 * time.Hour) 等硬编码等待方式——不可靠且不响应中断;
- 生产环境建议结合 context.Context 实现超时控制与取消传播,并添加日志与错误处理。
通过以上结构,你的 Go 程序即可稳定、可控地按 cron 规则持续执行任务,真正实现“后台定时服务”的语义。









