在 Golang,有没有办法每隔一段时间做重复性的工作?

Is there a way to do repetitive background tasks in Go? I'm thinking of something like Timer.schedule(task, delay, period) in Java. I know I can do this with a goroutine and Time.sleep(), but I'd like something that easily stopped.

Here's what I got, but looks ugly to me. Is there a cleaner/better way?

func oneWay() {
    var f func()
    var t *time.Timer

    f = func () {
        fmt.Println("doing stuff")
        t = time.AfterFunc(time.Duration(5) * time.Second, f)
    }

    t = time.AfterFunc(time.Duration(5) * time.Second, f)

    defer t.Stop()

    //simulate doing stuff
    time.Sleep(time.Minute)
}

转载于:https://stackoverflow.com/questions/16466320/is-there-a-way-to-do-repetitive-tasks-at-intervals-in-golang

The function time.NewTicker makes a channel that sends a periodic message, and provides a way to stop it. Use it something like this (untested):

ticker := time.NewTicker(5 * time.Second)
quit := make(chan struct{})
go func() {
    for {
       select {
        case <- ticker.C:
            // do stuff
        case <- quit:
            ticker.Stop()
            return
        }
    }
 }()

You can stop the worker by closing the quit channel: close(quit).

How about something like

package main

import (
    "fmt"
    "time"
)

func schedule(what func(), delay time.Duration) chan bool {
    stop := make(chan bool)

    go func() {
        for {
            what()
            select {
            case <-time.After(delay):
            case <-stop:
                return
            }
        }
    }()

    return stop
}

func main() {
    ping := func() { fmt.Println("#") }

    stop := schedule(ping, 5*time.Millisecond)
    time.Sleep(25 * time.Millisecond)
    stop <- true
    time.Sleep(25 * time.Millisecond)

    fmt.Println("Done")
}

Playground

Check out this library: https://github.com/robfig/cron

Example as below:

c := cron.New()
c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") })
c.AddFunc("@hourly",      func() { fmt.Println("Every hour") })
c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") })
c.Start()

If you do not care about tick shifting (depending on how long did it took previously on each execution) and you do not want to use channels, it's possible to use native range function.

i.e.

package main

import "fmt"
import "time"

func main() {
    go heartBeat()
    time.Sleep(time.Second * 5)
}
func heartBeat(){
    for range time.Tick(time.Second *1){
        fmt.Println("Foo")
    }
}

Playground

If you want to stop it in any moment ticker

ticker := time.NewTicker(500 * time.Millisecond)
    go func() {
        for range ticker.C {
            fmt.Println("Tick")
        }
    }()
time.Sleep(1600 * time.Millisecond)
ticker.Stop()

If you do not want to stop it tick:

 tick := time.Tick(500 * time.Millisecond)
for range tick {
        fmt.Println("Tick")
}

I use the following code:

package main

import (
    "fmt"
    "time"
)

func main() {
    now := time.Now()
    fmt.Println("\nToday:", now)

    after := now.Add(1 * time.Minute)
    fmt.Println("\nAdd 1 Minute:", after)

    for {
        fmt.Println("test")
        time.Sleep(10 * time.Second)

        now = time.Now()

        if now.After(after) {
            break
        }
    }

    fmt.Println("done")
}

It is more simple and works fine to me.

package main

import (
"github.com/jasonlvhit/gocron"
"github.com/sirupsen/logrus"
"time"
)

func main() {
s := gocron.NewScheduler()
s.Every(15).Seconds().Do(cron)
<-s.Start()
}

func cron() {
log := logrus.WithFields(logrus.Fields{
"key": "15",
"datatime": time.Now().Unix(),
})
log.WithFields(logrus.Fields{"data": 10}).Info("hello world!")
}