Golang如何正确的停止Ticker

Golang可以利用time包的Ticker实现定时器的作用,最近使用Ticker时,发现调用Ticker的Stop方法无法正确的停止Ticker,协程会阻塞在等待Ticker的C通道处,精简后的代码如下:

func UseTickerWrong() *time.Ticker {
    ticker := time.NewTicker(5 * time.Second)
    go func(ticker *time.Ticker) {
        for range ticker.C {
            fmt.Println("Ticker1....")
        }
        
        fmt.Println("Ticker1 Stop")
    }(ticker)
    
    return ticker
}
函数中我们创建一个5s的定时器,然后启动协程,在协程中我们读取Ticker的C通道,当定时时间到达时,该通道就会读到数据。我们在主函数中调用

func main() {
    ticker1 := UseTickerWrong()
    time.Sleep(20 * time.Second)
    ticker1.Stop()
}
输出结果为:

Ticker1....
Ticker1....
Ticker1....
Ticker1....

并没有最后的Ticker1 Stop,查看Ticker的Stop方法的说明会发现:

// Stop turns off a ticker. After Stop, no more ticks will be sent.
// Stop does not close the channel, to prevent a read from the channel succeeding
// incorrectly.

翻一下就是Stop会停止Ticker,停止后,Ticker不会再被发送,但是Stop不会关闭通道,防止读取通道发生错误。

Golang中从已经关闭的通道读取数据会发生错误,Ticker的通道不关闭,防止我们在不必要的时候读取了已经关闭的通道。那么,到底如何科学的停止ticker呢?可以看看下面的函数

func UserTicker() chan bool {
    ticker := time.NewTicker(5 * time.Second)
    
    stopChan := make(chan bool)
    go func(ticker *time.Ticker) {
        defer ticker.Stop()
        
        for {
            select {
                case <-ticker.C:
                    fmt.Println("Ticker2....")
                case stop := <-stopChan:
                    if stop {
                        fmt.Println("Ticker2 Stop")
                        return
                    }
            }
        }
    }(ticker)
    
    return stopChan
}
我们通过select读取两个通道,当stop通道读到true的时候,函数返回,由于使用了defer,会调用Ticker的Stop方法,之后从协程返回,主函数调用如下所示:

func main() {
    ch := UserTicker()
    time.Sleep(20 * time.Second)
    ch <- true
    close(ch)
}
输出如下所示

Ticker2....
Ticker2....
Ticker2....
Ticker2....
Ticker2 Stop

可以看到,我们可以正常退出协程了。
————————————————
版权声明:本文为CSDN博主「大鹏1987」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yjp19871013/article/details/82048944

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章