Is there a way to do repetitive tasks at intervals?

Go

Go Problem Overview


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

Go Solutions


Solution 1 - Go

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).

Solution 2 - Go

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

Solution 3 - Go

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][1] [1]: http://play.golang.org/p/UQ-ivZ_hZa

Solution 4 - Go

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()

Solution 5 - Go

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")
}

Solution 6 - Go

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.

Solution 7 - Go

A broader answer to this question might consider the Lego brick approach often used in Occam, and offered to the Java community via JCSP. There is a very good presentation by Peter Welch on this idea.

This plug-and-play approach translates directly to Go, because Go uses the same Communicating Sequential Process fundamentals as does Occam.

So, when it comes to designing repetitive tasks, you can build your system as a dataflow network of simple components (as goroutines) that exchange events (i.e. messages or signals) via channels.

This approach is compositional: each group of small components can itself behave as a larger component, ad infinitum. This can be very powerful because complex concurrent systems are made from easy to understand bricks.

Footnote: in Welch's presentation, he uses the Occam syntax for channels, which is ! and ? and these directly correspond to ch<- and <-ch in Go.

Solution 8 - Go

You can simply use gocron package like this:

package main

import (
 "fmt"
 "time"
 // go get github.com/go-co-op/gocron
 "github.com/go-co-op/gocron"
)

func main() {
 s := gocron.NewScheduler(time.UTC)
 s.Every(3).Seconds().Do(func() { fmt.Println("Every 3 seconds") })
 // you can start running the scheduler in two different ways:
 // starts the scheduler asynchronously
 s.StartAsync()
 // starts the scheduler and blocks current execution path
 // s.StartBlocking()
}

gocron: https://github.com/go-co-op/gocron

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSteve BriskView Question on Stackoverflow
Solution 1 - GoPaul HankinView Answer on Stackoverflow
Solution 2 - GoAlekcView Answer on Stackoverflow
Solution 3 - GoVolkerView Answer on Stackoverflow
Solution 4 - GoBrowny LinView Answer on Stackoverflow
Solution 5 - GoJohn Balvin AriasView Answer on Stackoverflow
Solution 6 - GoGustavo EmmelView Answer on Stackoverflow
Solution 7 - GoRick-777View Answer on Stackoverflow
Solution 8 - GoArsham AryaView Answer on Stackoverflow