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