Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ticker Stop behaviour in Golang

Tags:

If I am ranging over a ticker channel and call stop() the channel is stopped but not closed.

In this Example:

package main

import (
    "time"
    "log"
)

func main() {
    ticker := time.NewTicker(1 * time.Second)
    go func(){
        for _ = range ticker.C {
            log.Println("tick")
        }
        log.Println("stopped")
    }()
    time.Sleep(3 * time.Second)
    log.Println("stopping ticker")
    ticker.Stop()
    time.Sleep(3 * time.Second)
}

Running produces:

2013/07/22 14:26:53 tick
2013/07/22 14:26:54 tick
2013/07/22 14:26:55 tick
2013/07/22 14:26:55 stopping ticker

So that goroutine never exits. Is there a better way to handle this case? Should I care that the goroutine never exited?

like image 591
whatupdave Avatar asked Jul 22 '13 21:07

whatupdave


People also ask

How to Stop Ticker Golang?

The Stop() function in Go language is used to disable a ticker. So, after calling Stop() method no further ticks will be transmitted. And it doesn't close the channel, in order to avoid a concurrent go-routine reading from the channel from viewing an inaccurate “tick”.

How do you pause a ticker?

You stop ticker by attaching it to NULL instead of to some function. You may later re-attach thae same or another function to the same ticker.


2 Answers

Used a second channel as Volker suggested. This is what I ended up running with:

package main

import (
    "log"
    "time"
)

// Run the function every tick
// Return false from the func to stop the ticker
func Every(duration time.Duration, work func(time.Time) bool) chan bool {
    ticker := time.NewTicker(duration)
    stop := make(chan bool, 1)

    go func() {
        defer log.Println("ticker stopped")
        for {
            select {
            case time := <-ticker.C:
                if !work(time) {
                    stop <- true
                }
            case <-stop:
                return
            }
        }
    }()

    return stop
}

func main() {
    stop := Every(1*time.Second, func(time.Time) bool {
        log.Println("tick")
        return true
    })

    time.Sleep(3 * time.Second)
    log.Println("stopping ticker")
    stop <- true
    time.Sleep(3 * time.Second)
}
like image 121
whatupdave Avatar answered Sep 27 '22 19:09

whatupdave


Signal "done" on a second channel and select in your goroutine between ticker and done channel.

Depending on what you really want to do a better solution might exist, but this is hard to tell from the reduced demo code.

like image 39
Volker Avatar answered Sep 27 '22 19:09

Volker