37

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

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

4

6 回答 6

34

这是一个通用实现,可让您设置:

  • 间隔期
  • 小时打勾
  • 分钟打勾
  • 第二个打勾

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

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)
    }
}
于 2013-10-23T18:22:33.237 回答
23

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

链接: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()
}
于 2016-02-11T13:22:53.823 回答
20

@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()
    }
}
于 2016-09-02T15:29:16.150 回答
8

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

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

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

于 2017-08-10T01:45:14.037 回答
6

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

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

每天中午跑func一次。

  • 时期:time.Hour * 24
  • 抵消:time.Hour * 12

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

  • 时期: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)
    }
}
于 2019-05-15T20:06:01.673 回答
0

我正在使用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")
}
于 2021-07-31T06:02:38.183 回答