如何使用Golang实现定时器并发执行_Golang time.Ticker与goroutine示例

10次阅读

time.Ticker需配合goroutine使用,避免阻塞导致tick丢失;应加mutex防重入或context控制超时,并注意Stop、共享通道、退避等生产问题。

如何使用Golang实现定时器并发执行_Golang time.Ticker与goroutine示例

time.Ticker 不能直接并发执行任务

time.Ticker 本身只是按固定间隔发送时间戳的通道,它不启动 goroutine,也不处理并发。如果你在 for range ticker.C 循环里直接调用耗时函数,整个循环会被阻塞,后续 tick 就会积或丢失。

常见错误现象:定时任务越跑越慢、漏执行、甚至卡死。

  • 必须显式用 go func() { ... }() 启动新 goroutine 处理每次 tick
  • 注意闭包捕获变量问题:别直接在循环里用 for i := range ... { go func() { fmt.Println(i) }() },要用参数传入或定义局部变量
  • 如果任务可能超时,建议加 context.WithTimeout 控制单次执行生命周期

基础并发 ticker 示例(带防重入保护)

多数场景下,你不想让上次任务还没结束,下次就又触发——这会导致状态冲突或资源竞争。需要简单同步控制。

package main 

import ( "fmt" "sync" "time" )

func main() { ticker := time.NewTicker(2 * time.Second) defer ticker.Stop()

var mu sync.Mutex var busy bool  for range ticker.C {     mu.Lock()     if busy {         mu.Unlock()         fmt.Println("⚠️  上次任务还在运行,跳过本次执行")         continue     }     busy = true     mu.Unlock()      go func() {         defer func() {             mu.Lock()             busy = false             mu.Unlock()         }()         fmt.Printf("✅ 开始执行: %sn", time.Now().Format("15:04:05"))         time.Sleep(3 * time.Second) // 模拟耗时任务         fmt.Printf("✅ 执行完成: %sn", time.Now().Format("15:04:05"))     }() }

}

立即学习go语言免费学习笔记(深入)”;

用 context 控制单次任务超时与取消

当任务不可控(比如 http 请求、数据库查询),靠 busy 标志不够——goroutine 可能永远卡住。必须引入 context 实现可中断执行。

  • context.WithTimeout 是最常用方式,超时后自动 cancel
  • 所有支持 context 的标准库函数(如 http.Client.Dosql.DB.QueryContext)都应传入该 context
  • 不要忽略 ctx.Err() 检查,否则 timeout 不生效
package main 

import ( "context" "fmt" "time" )

func doWork(ctx context.Context) { select { case <-time.After(4 * time.Second): fmt.Println("✅ 任务正常完成") case <-ctx.Done(): fmt.Printf("❌ 任务被取消: %vn", ctx.Err()) return } }

func main() { ticker := time.NewTicker(3 * time.Second) defer ticker.Stop()

for range ticker.C {     ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)     go func() {         defer cancel()         fmt.Printf("? 启动任务: %sn", time.Now().Format("15:04:05"))         doWork(ctx)     }() }  time.Sleep(10 * time.Second) // 给足够时间观察输出

}

立即学习go语言免费学习笔记(深入)”;

生产环境要注意的几个坑

真实服务中,time.Ticker + goroutine 看似简单,但容易在边界条件下崩掉。

  • ticker.Stop() 必须调用,否则 goroutine 泄漏(尤其在 long-running service 中)
  • 不要把 ticker.C 直接传给多个 goroutine 共享读取——它不是线程安全的“广播通道”,会 panic
  • 如果任务失败频率高,考虑加退避逻辑(比如指数退避),避免疯狂重试打挂下游
  • runtime.GOMAXPROCSGOMAXPROCS 环境变量确认并发能力,别假设默认值够用

真正难的不是启动 goroutine,而是确保它干净退出、不泄漏、不干扰其他 tick、失败时有可观测性。这些细节往往要靠日志 + pprof + metrics 补全,光靠 ticker 本身做不到。

text=ZqhQzanResources