artk193
artk193

Reputation: 11

Best way to instantly restart a goroutine?

I have written the following functions:

func (timer *Timer) ticker() {
    for timer.time >= 0 {
        select {
        case <-timer.stopFlag:
            return
        default:
            timer.DataChannel <- timer.timeToString()
            timer.time--
            time.Sleep(time.Second)
        }
    }
}
func startLabelUpdateListener(timer *t.Timer, w *app.Window, label *string) {
    for time := range timer.DataChannel {
        *label = time
        w.Invalidate()
    }
}

It's a simple timer that is hooked up to a label that updates every time there is an update on the DataChanel. They're both called as goroutines.

Now is there a way to instantly stop the ticker and call it again, right after? Say I wanted to restart this timer. I want it to stop instantly, and fire up another ticker goroutine to take over.

I've tried the following but I'm having some issues:

timer.stopFlag <- true
go timer.ticker()
...

The problem with this is that it's not instant, since I'm using time.Sleep() go waits until the sleep is over which is not great for responsiveness as that introduces UI delay. Manually updating the label with something like dataChannel <- "some_time" means that if the user clicks the restart button with 0.9 sec sleep to go, the timer won't change for 1.9 seconds.

close(timer.stopFlag)
timer.stopFlag = make(chan bool)
go timer.ticker()
...

This is nice and instant (gets past the sleep or at least that's what it seems) but the problem is, the channel is now closed and reopening it somehow stops the previous goroutine from terminating and pressing the restart button several times stacks the goroutines

I've also considered having setting time.sleep to 0.1 of a second, and having time decrease every 10 iterations but that feels a little hacky. Is there a better solution?

Upvotes: 0

Views: 630

Answers (1)

Pamela&#39;s Workout
Pamela&#39;s Workout

Reputation: 26

Use time.Ticker instead of sleeping.

func (timer *Timer) ticker() {
    // Send the first.
    timer.DataChannel <- timer.timeToString()
    timer.time--

    // Loop sending each second. 
    t := time.NewTicker(time.Second)
    defer t.Stop()
    for timer.time >= 0 {
        select {
        case <-timer.stopFlag:
            return
        case <-t.C:
            timer.DataChannel <- timer.timeToString()
            timer.time--
        }
    }
}

Upvotes: 1

Related Questions