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

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