我正在通过开发任务计划来学习go-lang。我使用的cron library接受一个cron表达式和一个func作为参数来添加一个调度程序。
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
我正在开发的计划表根据yaml文件计划作业。所以我迭代这些作业来添加这样的日程表:
type Job struct { Name string Interval string } func DistributeJob(job Job) { log.Println("running", job, job.Interval) } func main() { //load config from yaml c := cron.New() for _, job := range config.Jobs { c.AddFunc("@every "+job.Interval, func() { DistributeJob(job) }) log.Println("Job " + job.Name + " has been scheduled!") } c.Start() select {} }
所有工作都按其间隔安排,但事实证明他们正在打印上一份工作的描述。例如,如果我安排两个作业,第一个间隔在3分钟,后一个间隔在1分钟。控制台打印:
12:01: Running latter 1min 12:02: Running latter 1min 12:03: Running latter 1min 12:03: Running latter 1min//this one should be the first job
我认为问题出在
func() { DistributeJob(job) })
似乎只需要上一份工作,但我无法弄清楚原因。我尝试使用
c.AddFunc("@every "+job.Interval, func(job JobType) { DistributeJob(job) }(job))
但由于无法用作值
而失败答案 0 :(得分:6)
虽然您没有使用goroutines,但您所犯的错误几乎与此处描述的错误相同:https://github.com/golang/go/wiki/CommonMistakes#using-closures-with-goroutines
引用:
上面循环中的val变量实际上是一个单独的变量,它接受每个slice元素的值。因为闭包只绑定到那个变量,所以很有可能当你运行这个代码时,你会看到每次迭代打印的最后一个元素而不是序列中的每个值,因为goroutines可能不会开始执行,直到循环之后。
因此,您尝试修复(将其作为参数传递给您的函数)原则上可以修复问题,但是您无法将带参数的函数传递给cron库 - 带参数的函数与不具有参数的函数不同(除此之外,通过添加()
,您实际上是事先调用函数并尝试传递其返回值。)
最简单的解决方法是为循环的每次迭代创建一个新变量,并避免整个问题,如下所示:
for _, job := range config.Jobs {
realJob := job // a new variable each time through the loop
c.AddFunc("@every "+realJob.Interval, func() {
DistributeJob(realJob)
})
log.Println("Job " + realJob.Name + " has been scheduled!")
}