【问题标题】:Golang: Implementing a cron / executing tasks at a specific timeGolang:在特定时间实现 cron / 执行任务
【发布时间】:2013-11-02 04:08:43
【问题描述】:

我一直在寻找有关如何实现允许您在 Go 中的特定时间执行任务的函数的示例,但我找不到任何东西。

我自己实现了一个,并在答案中分享它,以便其他人可以参考他们自己的实现。

【问题讨论】:

  • 无耻插件:也许你可以看看这个:极简 cron go-package: github.com/roylee0704/gron

标签: cron go scheduled-tasks crontab ticker


【解决方案1】:

这是一个通用的实现,可以让你设置:

  • 间隔周期
  • 小时数
  • 分钟数
  • 秒打勾

更新:(内存泄漏已修复)

import (
"fmt"
"time"
)

const INTERVAL_PERIOD time.Duration = 24 * time.Hour

const HOUR_TO_TICK int = 23
const MINUTE_TO_TICK int = 00
const SECOND_TO_TICK int = 03

type jobTicker struct {
    timer *time.Timer
}

func runningRoutine() {
    jobTicker := &jobTicker{}
    jobTicker.updateTimer()
    for {
        <-jobTicker.timer.C
        fmt.Println(time.Now(), "- just ticked")
        jobTicker.updateTimer()
    }
}

func (t *jobTicker) updateTimer() {
    nextTick := time.Date(time.Now().Year(), time.Now().Month(), 
    time.Now().Day(), HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, 0, time.Local)
    if !nextTick.After(time.Now()) {
        nextTick = nextTick.Add(INTERVAL_PERIOD)
    }
    fmt.Println(nextTick, "- next tick")
    diff := nextTick.Sub(time.Now())
    if t.timer == nil {
        t.timer = time.NewTimer(diff)
    } else {
        t.timer.Reset(diff)
    }
}

【讨论】:

  • 这个实现会泄漏内存。 > 停止代码以释放相关资源。
  • @Caleb 是对的,每次我们创建一个新代码时,旧代码永远不会被释放。更好的解决方案在这里:stackoverflow.com/a/39295990/2791115
  • @Caleb,谢谢!随时更新答案上的代码,以修复泄漏
  • 不是被垃圾回收了吗?
  • 这不能很好地处理 NTP 时钟调整...每日代码有时会触发两次。
【解决方案2】:

如果有人在这个问题上寻求快速解决方案。 我找到了一个简洁的库,可以很容易地安排工作。

链接:https://github.com/jasonlvhit/gocron

API 非常简单:

import (
    "fmt"
    "github.com/jasonlvhit/gocron"
)

func task() {
    fmt.Println("Task is being performed.")
}

func main() {
    s := gocron.NewScheduler()
    s.Every(2).Hours().Do(task)
    <- s.Start()
}

【讨论】:

    【解决方案3】:

    @Daniele B 提供的答案不够好,正如@Caleb 所说,实现会泄漏内存,因为每次我们创建一个新的股票代码时,旧的股票永远不会被释放。

    所以我包装了time.timer,并每次都重置它,这里是一个例子:

    package main
    
    import (
        "fmt"
        "time"
    )
    
    const INTERVAL_PERIOD time.Duration = 24 * time.Hour
    
    const HOUR_TO_TICK int = 23
    const MINUTE_TO_TICK int = 21
    const SECOND_TO_TICK int = 03
    
    type jobTicker struct {
        t *time.Timer
    }
    
    func getNextTickDuration() time.Duration {
        now := time.Now()
        nextTick := time.Date(now.Year(), now.Month(), now.Day(), HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, 0, time.Local)
        if nextTick.Before(now) {
            nextTick = nextTick.Add(INTERVAL_PERIOD)
        }
        return nextTick.Sub(time.Now())
    }
    
    func NewJobTicker() jobTicker {
        fmt.Println("new tick here")
        return jobTicker{time.NewTimer(getNextTickDuration())}
    }
    
    func (jt jobTicker) updateJobTicker() {
        fmt.Println("next tick here")
        jt.t.Reset(getNextTickDuration())
    }
    
    func main() {
        jt := NewJobTicker()
        for {
            <-jt.t.C
            fmt.Println(time.Now(), "- just ticked")
            jt.updateJobTicker()
        }
    }
    

    【讨论】:

      【解决方案4】:

      如果你熟悉的话,我已经创建了一个实际上支持 crontab 语法的包,例如:

      ctab := crontab.New()
      ctab.AddJob("*/5 * * * *", myFunc)
      ctab.AddJob("0 0 * * *", myFunc2)
      

      包链接:https://github.com/mileusna/crontab

      【讨论】:

      • 这真的很不错!感谢创建这个包:)
      【解决方案5】:

      这是另一个不需要第三方库的通用实现。

      免责声明:此实现适用于UTC。为了管理时区,必须对其进行修改。

      每天中午运行一次func

      • 期间:time.Hour * 24
      • 偏移量:time.Hour * 12

      每天在 03:40 (00:00 + 03:40) 和 15:40 (12:00 + 03:40) 运行两次 func

      • 期间:time.Hour * 12
      • 偏移量:time.Hour * 3 + time.Minute * 40

      更新(2020-01-28):

      变化:

      • context.Context 可用于取消,使其可测试。
      • time.Ticker 无需计算下一次执行的时间。
      package main
      
      import (
          "context"
          "time"
      )
      
      // Schedule calls function `f` with a period `p` offsetted by `o`.
      func Schedule(ctx context.Context, p time.Duration, o time.Duration, f func(time.Time)) {
          // Position the first execution
          first := time.Now().Truncate(p).Add(o)
          if first.Before(time.Now()) {
              first = first.Add(p)
          }
          firstC := time.After(first.Sub(time.Now()))
      
          // Receiving from a nil channel blocks forever
          t := &time.Ticker{C: nil}
      
          for {
              select {
              case v := <-firstC:
                  // The ticker has to be started before f as it can take some time to finish
                  t = time.NewTicker(p)
                  f(v)
              case v := <-t.C:
                  f(v)
              case <-ctx.Done():
                  t.Stop()
                  return
              }
          }
      
      }
      

      原文:

      package main
      
      import (
          "time"
      )
      
      // Repeat calls function `f` with a period `d` offsetted by `o`.
      func Repeat(d time.Duration, o time.Duration, f func(time.Time)) {
          next := time.Now().Truncate(d).Add(o)
          if next.Before(time.Now()) {
              next = next.Add(d)
          }
      
          t := time.NewTimer(next.Sub(time.Now()))
      
          for {
              v := <-t.C
              next = next.Add(d)
              t.Reset(next.Sub(time.Now()))
              f(v)
          }
      }
      

      【讨论】:

        【解决方案6】:

        我正在使用https://github.com/ehsaniara/gointerlock。它在分布式系统中也受支持,并具有内置的分发器锁 (Redis)

        import (
            "context"
            "fmt"
            "github.com/ehsaniara/gointerlock"
            "log"
            "time"
        )
        
        var job = gointerlock.GoInterval{
            Interval: 2 * time.Second,
            Arg:      myJob,
        }
        
        err := job.Run(ctx)
        if err != nil {
            log.Fatalf("Error: %s", err)
        }
        
        func myJob() {
            fmt.Println(time.Now(), " - called")
        }
        

        【讨论】:

          猜你喜欢
          • 2014-04-09
          • 2012-02-21
          • 1970-01-01
          • 1970-01-01
          • 2018-05-17
          • 2014-07-24
          • 1970-01-01
          • 1970-01-01
          • 2018-03-11
          相关资源
          最近更新 更多