Skip to content

Instantly share code, notes, and snippets.

@garenchan
Last active July 2, 2025 06:27
Show Gist options
  • Save garenchan/c9f4061b4c97403f188dfc31ada041ff to your computer and use it in GitHub Desktop.
Save garenchan/c9f4061b4c97403f188dfc31ada041ff to your computer and use it in GitHub Desktop.
github.com/robfig/cron/v3 定时任务串行,并尽可能保证间隔interval执行
// delayIfStillRunning 让定时任务串行执行,上一次执行完毕后,至少等待interval后才会执行下一次
func (m *Monitor) delayIfStillRunning() cron.JobWrapper {
return func(j cron.Job) cron.Job {
// 保证任务的串行执行
var mu sync.RWMutex
// cron会每隔interval创建一个goroutine来执行定时任务,如果简单使用mu
// 来保证串行执行,并且当任务耗时比较严重时,会导致大量goroutine堆积在mu.Lock()上,
// 随着服务长时间运行,可能出现goroutine耗尽,最终导致程序崩溃。事实上,只需要有一个goroutine等待上一次任务结束
var hasWaiter bool
var waiterMu sync.Mutex
return cron.FuncJob(func() {
waiterMu.Lock()
if hasWaiter {
waiterMu.Unlock()
return
}
hasWaiter = true
waiterMu.Unlock()
start := time.Now()
// log.Logger.Debug().Msgf("[cron][%+v][%s] start to wait lock.", j, start.String())
mu.Lock()
defer mu.Unlock()
if dur := time.Since(start); dur > 3*time.Second {
log.Logger.Warn().Msgf("[cron][%+v][%s] The interval time may be set too short.", j, start.String())
select {
case <-m.ctx.Done():
return
case <-time.After(time.Duration(m.interval) * time.Second):
}
}
waiterMu.Lock()
hasWaiter = false
waiterMu.Unlock()
// log.Logger.Debug().Msgf("[cron][%+v][%s] start to run cron.", j, start.String())
j.Run()
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment