douji2520 2015-03-10 01:50
浏览 44
已采纳

无法在for循环中为匿名函数分配变量

I'm studying go-lang by developing a task schedular. The cron library I use accepts a cron expression and a func as parameters to add a scheduler.

c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })

The schedular I am developing schedules jobs according to a yaml file. So I iterate the jobs to add schedulars like this:

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 {}
}

All jobs are scheduled on their intervals but it turns out that they're printing the last job's description. For example, if I schedule two jobs, the first interval on 3min and the latter interval on 1min. The console prints:

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

I think the problem is at

    func() {
        DistributeJob(job)
    })

It seems it only takes the last job but I can't figure out why. I tried using

    c.AddFunc("@every "+job.Interval, func(job JobType) {
        DistributeJob(job)
    }(job))

but it fails due to cannot used as value

  • 写回答

1条回答 默认 最新

  • dtddjq9637 2015-03-10 01:55
    关注

    While you are not using goroutines, the mistake you are making is almost identical to the one described here: https://github.com/golang/go/wiki/CommonMistakes#using-closures-with-goroutines

    To quote:

    The val variable in the above loop is actually a single variable that takes on the value of each slice element. Because the closures are all only bound to that one variable, there is a very good chance that when you run this code you will see the last element printed for every iteration instead of each value in sequence, because the goroutines will probably not begin executing until after the loop.

    So your attempted fix (to pass it as a parameter to your function) would in principle fix the problem, however you cannot pass a function with parameters to the cron library - a function with parameters is a different type from one without (in addition to which by adding the () you are actually calling the function in advance and trying to pass its return value).

    The simplest fix is to create a new variable for every iteration of the loop and avoid the whole problem, like so:

    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!")
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100
  • ¥15 关于#hadoop#的问题
  • ¥15 (标签-Python|关键词-socket)
  • ¥15 keil里为什么main.c定义的函数在it.c调用不了
  • ¥50 切换TabTip键盘的输入法
  • ¥15 可否在不同线程中调用封装数据库操作的类