Home > Blockchain >  How does Go invoke Ticker by interval?
How does Go invoke Ticker by interval?

Time:10-26

Since I am not experinced Go developer, I didn't understand way of working with Ticker. I have following scenario:

A go web service running on specific port 8080, it is getting data from another applications and processing the data. So far so good, but I have a another sendData function in this web service which loop through the some files and send them to another extern service. I am trying to call the sendData() function every 1 minute. Here is how main function looks without Tickers:

func main() {

    http.HandleFunc("/data", headers)          //line 1
    log.Printf("Ready for data ...%s\n", 8080) //line 2
    http.ListenAndServe(":8080", nil)          //line 3
}

If I add the Ticker after line 2 it's keeping loop infinitively. If I add after line 3, the programm is not invoking the Ticker. Any idea how to handle this?

The Ticker part

ticker := schedule(sendData, time.Second, done)
time.Sleep(60 * time.Second)
close(done)
ticker.Stop()

and the schedule from

func schedule(f func(), interval time.Duration, done <-chan bool) *time.Ticker {
    ticker := time.NewTicker(interval)
    go func() {
        for {
            select {
            case <-ticker.C:
                f()
            case <-done:
                return
            }
        }
    }()
    return ticker

So basically I want to sendData evert minute or hour etc. Could someone explain how internally Ticker works?

CodePudding user response:

http.ListenAndServe(":8080", nil) runs an infinite for loop listening for inbound connections, that's why the ticker is not invoked if you call it afterwards.

And then here

ticker := schedule(sendData, time.Second, done)
time.Sleep(60 * time.Second)
close(done)
ticker.Stop()

you're exiting the loop inside schedule() after 60 seconds, so your ticker will run only once or won't run at all (depending on whether the done channel receives the value before or later that the ticker ticks, as they are concurrent we cannot determine their order)


So what you want is the following

func main() {
    http.HandleFunc("/data", headers)

    ticker := time.NewTicker(time.Minute)
    go schedule(ticker)

    log.Printf("Ready for data ...%s\n", 8080)
    http.ListenAndServe(":8080", nil)
}

func schedule(ticker *time.Ticker) {
    for {
        // This blocks until a value is received, the ticker
        // sends a value to it every one minute (or the interval specified)
        <-ticker.C
        fmt.Println("Tick")
    }
}

As you may have noticed, once the server connection is interrupted the program will terminate so there's no point on having a done channel to exit the loop.

Try it here

CodePudding user response:

You are on the right track - you just need to wrap the ticker declaration in a self executing function and then run it as a goroutine. ListenAndServe and Schedule are both blocking tasks, so they need to run on separate go routines. Luckily go makes this really simple to achieve.

Note - this sample code is meant to stay as close to your example as possible. I would recommend separating the declaration of the ticker from the schedule func.

func main() {

    http.HandleFunc("/data", func(w http.ResponseWriter, req *http.Request) {}) //line 1

    var done chan bool

    go func() {
        ticker := schedule(func() { fmt.Println("Tick") }, time.Second, done)
        time.Sleep(60 * time.Second)
        close(done)
        ticker.Stop()
    }()

    fmt.Printf("Ready for data ...%v\n", 8080) //line 2
    http.ListenAndServe(":8080", nil)          //line 3
}
  • Related