leoOrion
leoOrion

Reputation: 1957

Ticker should not execute if already running

I have some function that has to run periodically. I have used a ticker for this. But if the ticker is already running, and the time interval passes again, it should not execute again.

package main

import (
    "fmt"
    "time"
)

func main() {
    ticker := time.NewTicker(3*time.Second)
    flag := 0
    defer ticker.Stop()
    for {
        select {
        case t := <-ticker.C:
            flag = flag + 1
            if (flag % 2 ==0 ) {
                time.Sleep(time.Second*4)
            }   
            fmt.Println("Current time: ", t)
        }
    }
}

https://play.golang.org/p/2xV2MYInn4I

In the playground, the ticker prints every 3 seconds, but every even turn of the ticker the job takes more time than the interval. I expect it to not run then and drop those ticks.

How do I do this?

Upvotes: 5

Views: 3605

Answers (2)

Peter
Peter

Reputation: 31681

Since Go 1.23 Ticker channels are unbuffered and this answer does not apply.

The ticker channel is buffered, which is why you may see multiple triggers right one after the other. You can prevent that by simply transfering the ticker's values to an unbuffered channel (note also that the time.Time value received from the ticker is not the current time but the time of the last tick):

package main

import (
    "fmt"
    "time"
)

func main() {
    c := make(chan time.Time) // unbuffered
    ticker := time.NewTicker(3 * time.Second)
    defer ticker.Stop()

    go func() {
        for t := range ticker.C {
            select {
            case c <- t:
            default:
            }
        }
    }()

    for flag := 0; flag < 8; flag++ {
        <-c

        if flag%2 == 0 {
            time.Sleep(time.Second * 4)
        }
        fmt.Println("Current time: ", time.Now())
    }
}

// Output:
// Current time:  2020-02-19 12:21:57.095433032 +0100 CET m=+3.000213350
// Current time:  2020-02-19 12:22:04.095585208 +0100 CET m=+10.000365520
// Current time:  2020-02-19 12:22:06.095363327 +0100 CET m=+12.000143680
// Current time:  2020-02-19 12:22:13.095605268 +0100 CET m=+19.000385598
// Current time:  2020-02-19 12:22:15.095371885 +0100 CET m=+21.000152174
// Current time:  2020-02-19 12:22:22.095537562 +0100 CET m=+28.000317857
// Current time:  2020-02-19 12:22:24.095431317 +0100 CET m=+30.000211625
// Current time:  2020-02-19 12:22:31.095524308 +0100 CET m=+37.000304595

Try it on the playground: https://play.golang.org/p/jDe5uJiRVe2

Upvotes: 6

Kelsnare
Kelsnare

Reputation: 705

sleeping inside the same goroutine merely delays execution. ticker meanwhile runs in a separate goroutine. So even if you used a global variable to maintain an execution state - it will not give you your desired result with sleep. However migrating the whole "sleeping" in a separate goroutine yields:

package main

import (
    "fmt"
    "time"
)

type Tick struct {
    ticker *time.Ticker
    executing bool
}

func somethingYouWantToDo(tick *Tick, flag *int, t time.Time) {
    if tick.executing {
        return
    }

    tick.executing = true

    *flag = *flag + 1

    if (*flag % 2 ==0 ) {
                time.Sleep(time.Second*4)
    }   
        fmt.Println("Current time: ", t)
    tick.executing = false
}

func main() {
    tick := &Tick{
        ticker: time.NewTicker(3*time.Second),
    }
    flag := 0
    defer tick.ticker.Stop()
    for {
        select {
        case t := <-tick.ticker.C:
            go somethingYouWantToDo(tick, &flag, t)
        }
    }
}
// output
// Current time:  2009-11-10 23:00:03 +0000 UTC m=+3.000000001
// Current time:  2009-11-10 23:00:06 +0000 UTC m=+6.000000001
// Current time:  2009-11-10 23:00:12 +0000 UTC m=+12.000000001
// Current time:  2009-11-10 23:00:15 +0000 UTC m=+15.000000001
// Current time:  2009-11-10 23:00:21 +0000 UTC m=+21.000000001
// Current time:  2009-11-10 23:00:24 +0000 UTC m=+24.000000001
// Current time:  2009-11-10 23:00:30 +0000 UTC m=+30.000000001
// Current time:  2009-11-10 23:00:33 +0000 UTC m=+33.000000001
// Current time:  2009-11-10 23:00:39 +0000 UTC m=+39.000000001
// Current time:  2009-11-10 23:00:42 +0000 UTC m=+42.000000001
// Current time:  2009-11-10 23:00:48 +0000 UTC m=+48.000000001
// Current time:  2009-11-10 23:00:51 +0000 UTC m=+51.000000001
// Current time:  2009-11-10 23:00:57 +0000 UTC m=+57.000000001
// Current time:  2009-11-10 23:01:00 +0000 UTC m=+60.000000001
// Current time:  2009-11-10 23:01:06 +0000 UTC m=+66.000000001
// Current time:  2009-11-10 23:01:09 +0000 UTC m=+69.000000001

Try it on the playground

Upvotes: 4

Related Questions