
本文介绍使用 time.ticker 实现多任务差异化周期调度,通过 goroutine 避免阻塞,并安全执行 shell 命令、捕获输出、处理错误,适用于监控采集等场景。
本文介绍使用 time.ticker 实现多任务差异化周期调度,通过 goroutine 避免阻塞,并安全执行 shell 命令、捕获输出、处理错误,适用于监控采集等场景。
在 Go 中实现函数的周期性执行,推荐使用标准库中的 time.Ticker —— 它提供高精度、低开销的定时触发机制。针对不同命令(如 uptime 每 5 秒、date 每 10 秒)需独立调度的需求,不应共用单个 ticker,而应为每个任务创建专属 ticker,并配合 select 语句实现非阻塞监听。
以下是一个健壮、可扩展的实现示例:
package main
import (
"log"
"os/exec"
"time"
)
// run 执行 shell 命令并返回标准输出(错误时记录日志但不 panic)
func run(cmdStr string) []byte {
cmd := exec.Command("sh", "-c", cmdStr)
out, err := cmd.Output()
if err != nil {
log.Printf("command %q failed: %v, stderr: %s", cmdStr, err, string(cmd.Stderr))
return nil
}
return out
}
func main() {
// 启动两个独立 ticker
uptimeTicker := time.NewTicker(5 * time.Second)
defer uptimeTicker.Stop()
dateTicker := time.NewTicker(10 * time.Second)
defer dateTicker.Stop()
// 使用 goroutine 并发执行,避免命令耗时导致 ticker 积压
go func() {
for range uptimeTicker.C {
if out := run("uptime"); out != nil {
log.Printf("uptime output: %s", string(out))
// ✅ 此处可追加数据聚合、写入文件、上报 HTTP 等逻辑
}
}
}()
go func() {
for range dateTicker.C {
if out := run("date"); out != nil {
log.Printf("date output: %s", string(out))
}
}
}()
// 主 goroutine 阻塞,防止程序退出
select {}
}
⚠️ 关键注意事项:
-
切勿在 select 主循环中直接调用阻塞型
run():若某次exec.Command耗时超过 ticker 间隔(如网络延迟、命令卡死),会导致后续 tick 被跳过或积压; -
使用
sh -c包装命令:确保支持管道、重定向等 shell 特性(原问题中exec.Command(cmd)无法解析含空格或符号的命令); -
错误处理要降级而非
log.Fatal:生产环境应记录错误并继续运行,避免单点失败导致整个调度器崩溃; -
务必调用
ticker.Stop():虽本例中程序长期运行,但在可终止服务(如 CLI 工具)中需显式释放资源; -
如需共享状态(如汇总结果),请配合
sync.Mutex或 channel 安全传递,避免竞态。
该模式可轻松扩展:增加新 ticker、将命令与间隔封装为结构体、通过配置文件动态加载任务,是构建轻量级运维采集器的理想起点。










