立即重新启动goroutine的最佳方法是什么?

huangapple go评论86阅读模式
英文:

Best way to instantly restart a goroutine?

问题

我已经编写了以下函数:

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

这是一个简单的计时器,它与一个标签连接在一起,每当DataChannel上有更新时,标签就会更新。它们都作为goroutine调用。

现在有没有办法立即停止计时器并立即再次调用它?比如说我想重新启动这个计时器。我希望它能立即停止,并启动另一个ticker goroutine来接管。

我尝试了以下方法,但遇到了一些问题:

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

这种方法的问题是它不是立即停止的,因为我在使用time.Sleep(),go会等待sleep结束,这对于响应性来说不太好,因为会引入UI延迟。手动更新标签,例如dataChannel <- "some_time",意味着如果用户在0.9秒的sleep后点击重新启动按钮,计时器在1.9秒后才会改变。

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

这种方法是立即的(似乎可以跳过sleep),但问题是通道现在已经关闭,重新打开它会阻止先前的goroutine终止,多次按下重新启动按钮会堆叠goroutine。

我还考虑过将time.Sleep设置为0.1秒,并且每10次迭代减少一次时间,但感觉有点笨拙。有没有更好的解决方案?

英文:

I have written the following functions:

func (timer *Timer) ticker() {
	for timer.time &gt;= 0 {
		select {
		case &lt;-timer.stopFlag:
			return
		default:
			timer.DataChannel &lt;- 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 &lt;- 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 &lt;- &quot;some_time&quot; 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?

答案1

得分: 1

使用 time.Ticker 替代睡眠。

func (timer *Timer) ticker() {
    // 发送第一个时间。
    timer.DataChannel <- timer.timeToString()
    timer.time--

    // 循环每秒发送时间。
    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--
        }
    }
}
英文:

Use time.Ticker instead of sleeping.

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

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

huangapple
  • 本文由 发表于 2022年1月15日 08:40:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/70717999.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定