Golang中的Ticker停止行为

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

Ticker Stop behaviour in Golang

问题

如果我正在遍历一个ticker通道并调用stop(),通道会停止但不会关闭。

在这个例子中:

  1. package main
  2. import (
  3. "time"
  4. "log"
  5. )
  6. func main() {
  7. ticker := time.NewTicker(1 * time.Second)
  8. go func(){
  9. for _ = range ticker.C {
  10. log.Println("tick")
  11. }
  12. log.Println("stopped")
  13. }()
  14. time.Sleep(3 * time.Second)
  15. log.Println("stopping ticker")
  16. ticker.Stop()
  17. time.Sleep(3 * time.Second)
  18. }

运行结果为:

  1. 2013/07/22 14:26:53 tick
  2. 2013/07/22 14:26:54 tick
  3. 2013/07/22 14:26:55 tick
  4. 2013/07/22 14:26:55 stopping ticker

因此,该goroutine永远不会退出。有没有更好的处理这种情况的方法?我应该关心该goroutine永远不会退出吗?

英文:

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

In this Example:

  1. package main
  2. import (
  3. "time"
  4. "log"
  5. )
  6. func main() {
  7. ticker := time.NewTicker(1 * time.Second)
  8. go func(){
  9. for _ = range ticker.C {
  10. log.Println("tick")
  11. }
  12. log.Println("stopped")
  13. }()
  14. time.Sleep(3 * time.Second)
  15. log.Println("stopping ticker")
  16. ticker.Stop()
  17. time.Sleep(3 * time.Second)
  18. }

Running produces:

  1. 2013/07/22 14:26:53 tick
  2. 2013/07/22 14:26:54 tick
  3. 2013/07/22 14:26:55 tick
  4. 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?

答案1

得分: 23

使用了第二个通道如Volker建议的。这是我最终运行的代码:

  1. package main
  2. import (
  3. "log"
  4. "time"
  5. )
  6. // 每个滴答运行一次函数
  7. // 从函数中返回false以停止滴答器
  8. func Every(duration time.Duration, work func(time.Time) bool) chan bool {
  9. ticker := time.NewTicker(duration)
  10. stop := make(chan bool, 1)
  11. go func() {
  12. defer log.Println("滴答器停止")
  13. for {
  14. select {
  15. case time := <-ticker.C:
  16. if !work(time) {
  17. stop <- true
  18. }
  19. case <-stop:
  20. return
  21. }
  22. }
  23. }()
  24. return stop
  25. }
  26. func main() {
  27. stop := Every(1*time.Second, func(time.Time) bool {
  28. log.Println("滴答")
  29. return true
  30. })
  31. time.Sleep(3 * time.Second)
  32. log.Println("停止滴答器")
  33. stop <- true
  34. time.Sleep(3 * time.Second)
  35. }
英文:

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

  1. package main
  2. import (
  3. &quot;log&quot;
  4. &quot;time&quot;
  5. )
  6. // Run the function every tick
  7. // Return false from the func to stop the ticker
  8. func Every(duration time.Duration, work func(time.Time) bool) chan bool {
  9. ticker := time.NewTicker(duration)
  10. stop := make(chan bool, 1)
  11. go func() {
  12. defer log.Println(&quot;ticker stopped&quot;)
  13. for {
  14. select {
  15. case time := &lt;-ticker.C:
  16. if !work(time) {
  17. stop &lt;- true
  18. }
  19. case &lt;-stop:
  20. return
  21. }
  22. }
  23. }()
  24. return stop
  25. }
  26. func main() {
  27. stop := Every(1*time.Second, func(time.Time) bool {
  28. log.Println(&quot;tick&quot;)
  29. return true
  30. })
  31. time.Sleep(3 * time.Second)
  32. log.Println(&quot;stopping ticker&quot;)
  33. stop &lt;- true
  34. time.Sleep(3 * time.Second)
  35. }

答案2

得分: 15

在你的goroutine中,在ticker和done通道之间选择,并在第二个通道上发送信号“done”。

根据你真正想要做什么,可能存在更好的解决方案,但从简化的演示代码中很难判断。

英文:

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.

答案3

得分: 8

你可以这样做。

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func startTicker(f func()) chan bool {
  7. done := make(chan bool, 1)
  8. go func() {
  9. ticker := time.NewTicker(time.Second * 1)
  10. defer ticker.Stop()
  11. for {
  12. select {
  13. case <-ticker.C:
  14. f()
  15. case <-done:
  16. fmt.Println("done")
  17. return
  18. }
  19. }
  20. }()
  21. return done
  22. }
  23. func main() {
  24. done := startTicker(func() {
  25. fmt.Println("tick...")
  26. })
  27. time.Sleep(5 * time.Second)
  28. close(done)
  29. time.Sleep(5 * time.Second)
  30. }
英文:

you can do like this.

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;time&quot;
  5. )
  6. func startTicker(f func()) chan bool {
  7. done := make(chan bool, 1)
  8. go func() {
  9. ticker := time.NewTicker(time.Second * 1)
  10. defer ticker.Stop()
  11. for {
  12. select {
  13. case &lt;-ticker.C:
  14. f()
  15. case &lt;-done:
  16. fmt.Println(&quot;done&quot;)
  17. return
  18. }
  19. }
  20. }()
  21. return done
  22. }
  23. func main() {
  24. done := startTicker(func() {
  25. fmt.Println(&quot;tick...&quot;)
  26. })
  27. time.Sleep(5 * time.Second)
  28. close(done)
  29. time.Sleep(5 * time.Second)
  30. }

答案4

得分: 0

如果你需要节省更多的空间,可以使用空结构体(struct{})的通道,它不占用内存。正如上面提到的,不要在其中发送任何东西,只需关闭通道,实际上发送的是零值。

英文:

If you need to save more space, use channels of empty structs (struct{}) which costs no memory. And as mentioned above, don't send something in it - just close, which actually sends zero value.

答案5

得分: 0

我认为这是一个很好的答案

  1. func doWork(interval time.Duration, work func(), stop chan struct{}, done func()) {
  2. t := time.NewTicker(interval)
  3. for {
  4. select {
  5. case <-t.C:
  6. work()
  7. case _, more := <-stop:
  8. if !more {
  9. done()
  10. }
  11. return
  12. }
  13. }
  14. }
  15. func Test_workTicker(t *testing.T) {
  16. var wg sync.WaitGroup
  17. wg.Add(1)
  18. stop := make(chan struct{})
  19. go doWork(
  20. 500*time.Millisecond,
  21. func() { fmt.Println("做工作") },
  22. stop,
  23. func() {
  24. fmt.Println("在go协程中完成")
  25. wg.Done()
  26. },
  27. )
  28. time.Sleep(5 * time.Second)
  29. close(stop)
  30. wg.Wait()
  31. fmt.Println("在主函数中完成")
  32. }
英文:

I think this is a nice answer

  1. func doWork(interval time.Duration, work func(), stop chan struct{}, done func()) {
  2. t := time.NewTicker(interval)
  3. for {
  4. select {
  5. case &lt;-t.C:
  6. work()
  7. case _, more := &lt;-stop:
  8. if !more {
  9. done()
  10. }
  11. return
  12. }
  13. }
  14. }
  15. func Test_workTicker(t *testing.T) {
  16. var wg sync.WaitGroup
  17. wg.Add(1)
  18. stop := make(chan struct{})
  19. go doWork(
  20. 500*time.Millisecond,
  21. func() { fmt.Println(&quot;do work&quot;) },
  22. stop,
  23. func() {
  24. fmt.Println(&quot;done in go routine&quot;)
  25. wg.Done()
  26. },
  27. )
  28. time.Sleep(5 * time.Second)
  29. close(stop)
  30. wg.Wait()
  31. fmt.Println(&quot;done in main&quot;)
  32. }

huangapple
  • 本文由 发表于 2013年7月23日 05:28:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/17797754.html
匿名

发表评论

匿名网友

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

确定