Golang中的Ticker停止行为

wha*_*ave 16 go ticker

如果我在一个自动收报机通道上调用并调用stop(),则该通道会停止但不会关闭.

在这个例子中:

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)
}
Run Code Online (Sandbox Code Playgroud)

跑步产生:

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
Run Code Online (Sandbox Code Playgroud)

所以goroutine永远不会退出.有没有更好的方法来处理这种情况?我应该关心goroutine从未退出吗?

wha*_*ave 15

Volker建议使用第二个频道.这是我最终运行的:

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)
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你的工作需要4秒钟,那么你的goroutine会陷入僵局,并且它会被困在试图写入频道的时候,它是唯一的读者.你真的只想要一个`for {}`的状态变量 - 并且不要发送停止通道,只需关闭它. (6认同)

Vol*_*ker 12

在第二个通道上发出"完成"信号,并在您的goroutine中选择自动收报机和完成通道.

根据您真正想要做的事情,可能存在更好的解决方案,但这很难从简化的演示代码中看出来.


aho*_*Lic 5

你可以这样做。

package main

import (
    "fmt"
    "time"
)

func startTicker(f func()) chan bool {
    done := make(chan bool, 1)
    go func() {
        ticker := time.NewTicker(time.Second * 1)
        defer ticker.Stop()
        for {
            select {
            case <-ticker.C:
                f()
            case <-done:
                fmt.Println("done")
                return
            }
        }
    }()
    return done
}

func main() {
    done := startTicker(func() {
        fmt.Println("tick...")
    })
    time.Sleep(5 * time.Second)
    close(done)
    time.Sleep(5 * time.Second)
}
Run Code Online (Sandbox Code Playgroud)