Golang定時器Timer與Ticker的使用詳解
1、概述
在 Go 里有很多種定時器的使用方法,像常規(guī)的 Timer、Ticker 對象,以及經(jīng)常會看到的 time.After(d Duration) 和 time.Sleep(d Duration) 方法。以上這些定時器的使用方法都來自Golang 原生 time 包,使用time包可以用來執(zhí)行一些定時任務(wù)或者是周期性的任務(wù)。
2、定時器使用
2.1 Timer 相關(guān)
func NewTimer(d Duration) *Timer func (t *Timer) Reset(d Duration) bool func (t *Timer) Stop() bool func After(d Duration) <-chan Time func AfterFunc(d Duration, f func()) *Timer //timer例子 func main() { timer := time.NewTimer(3 * time.Second) //啟動定時器,生產(chǎn)一個Timer對象 select { case <-timer.C: fmt.Println("3秒執(zhí)行任務(wù)") } timer.Stop() // 不再使用了,結(jié)束它 } //time.After例子 func main() { tChannel := time.After(3 * time.Second) // 其內(nèi)部其實是生成了一個Timer對象 select { case <-tChannel: fmt.Println("3秒執(zhí)行任務(wù)") } } func main() { timer := time.NewTimer(3 * time.Second) for { timer.Reset(4 * time.Second) // 這樣來復(fù)用 timer 和修改執(zhí)行時間 select { case <-timer.C: fmt.Println("每隔4秒執(zhí)行任務(wù)") } } }
從上面可以看出來 Timer 允許再次被啟用,而 time.After 返回的是一個 channel,將不可復(fù)用。
而且需要注意的是 time.After 本質(zhì)上是創(chuàng)建了一個新的 Timer 結(jié)構(gòu)體,只不過暴露出去的是結(jié)構(gòu)體里的 channel 字段而已。
因此如果在 for{...}里循環(huán)使用了 time.After,將會不斷的創(chuàng)建 Timer。如下的使用方法就會帶來性能問題:
錯誤使用:
for 里的 time.After 將會不斷的創(chuàng)建 Timer 對象,雖然最終會回收,但是會造成無意義的cpu資源消耗
func main() { for { select { case <-time.After(3 * time.Second): fmt.Println("每隔3秒執(zhí)行一次") } } }
正確使用:
func main() { timer := time.NewTimer(3 * time.Second) for { timer.Reset(3 * time.Second) // 這里復(fù)用了 timer select { case <-timer.C: fmt.Println("每隔3秒執(zhí)行一次") } } }
2.2 Ticker 相關(guān)
這里的 Ticker 跟 Timer 的不同之處,就在于 Ticker 時間達(dá)到后不需要人為調(diào)用 Reset 方法,會自動續(xù)期。
func NewTicker(d Duration) *Ticker func Tick(d Duration) <-chan Time func (t *Ticker) Stop() func main() { ticker := time.NewTicker(3 * time.Second) for range ticker.C { fmt.Print("每隔3秒執(zhí)行任務(wù)") } ticker.Stop() }
錯誤使用:
func main() { for { select { case <-time.Tick(3 * time.Second): // 這里會不斷生成 ticker,而且 ticker 會進(jìn)行重新調(diào)度,造成泄漏 fmt.Println("每隔3秒執(zhí)行一次") } } }
3、定時器使用示例
3.1 Ticker定時器
package main import ( "fmt" "time" ) func main() { // Ticker 包含一個通道字段C,每隔時間段 d 就向該通道發(fā)送當(dāng)時系統(tǒng)時間。 // 它會調(diào)整時間間隔或者丟棄 tick 信息以適應(yīng)反應(yīng)慢的接收者。 // 如果d <= 0會觸發(fā)panic。關(guān)閉該 Ticker 可以釋放相關(guān)資源。 ticker1 := time.NewTicker(5 * time.Second) // 一定要調(diào)用Stop(),回收資源 defer ticker1.Stop() go func(t *time.Ticker) { for { // 每5秒中從chan t.C 中讀取一次 <-t.C fmt.Println("Ticker:", time.Now().Format("2006-01-02 15:04:05")) } }(ticker1) time.Sleep(30 * time.Second) fmt.Println("ok") }
執(zhí)行結(jié)果:
Ticker: 2022-01-18 13:39:30
Ticker: 2022-01-18 13:39:35
Ticker: 2022-01-18 13:39:40
Ticker: 2022-01-18 13:39:45
Ticker: 2022-01-18 13:39:50
ok
Ticker: 2022-01-18 13:39:55
可以看到每次執(zhí)行的時間間隔都是一樣的,由于main線程結(jié)束導(dǎo)致程序結(jié)束。
3.2 Timer定時器
package main import ( "fmt" "time" ) func main() { // NewTimer 創(chuàng)建一個 Timer,它會在最少過去時間段 d 后到期,向其自身的 C 字段發(fā)送當(dāng)時的時間 timer1 := time.NewTimer(5 * time.Second) fmt.Println("開始時間:", time.Now().Format("2006-01-02 15:04:05")) go func(t *time.Timer) { times := 0 for { <-t.C fmt.Println("timer", time.Now().Format("2006-01-02 15:04:05")) // 從t.C中獲取數(shù)據(jù),此時time.Timer定時器結(jié)束。如果想再次調(diào)用定時器,只能通過調(diào)用 Reset() 函數(shù)來執(zhí)行 // Reset 使 t 重新開始計時,(本方法返回后再)等待時間段 d 過去后到期。 // 如果調(diào)用時 t 還在等待中會返回真;如果 t已經(jīng)到期或者被停止了會返回假。 times++ // 調(diào)用 reset 重發(fā)數(shù)據(jù)到chan C fmt.Println("調(diào)用 reset 重新設(shè)置一次timer定時器,并將時間修改為2秒") t.Reset(2 * time.Second) if times > 3 { fmt.Println("調(diào)用 stop 停止定時器") t.Stop() } } }(timer1) time.Sleep(30 * time.Second) fmt.Println("結(jié)束時間:", time.Now().Format("2006-01-02 15:04:05")) fmt.Println("ok") }
執(zhí)行結(jié)果:
開始時間: 2022-01-18 13:25:43
timer 2022-01-18 13:25:48
調(diào)用 reset 重新設(shè)置一次timer定時器,并將時間修改為2秒
timer 2022-01-18 13:25:50
調(diào)用 reset 重新設(shè)置一次timer定時器,并將時間修改為2秒
timer 2022-01-18 13:25:52
調(diào)用 reset 重新設(shè)置一次timer定時器,并將時間修改為2秒
timer 2022-01-18 13:25:54
調(diào)用 reset 重新設(shè)置一次timer定時器,并將時間修改為2秒
調(diào)用 stop 停止定時器
結(jié)束時間: 2022-01-18 13:26:13
ok
可以看到,第一次執(zhí)行時間為5秒以后。然后通過調(diào)用 time.Reset() 方法再次激活定時器,定時時間為2秒,最后通過調(diào)用 time.Stop() 把前面的定時器取消掉。
4、總結(jié)
ticker定時器表示每隔一段時間就執(zhí)行一次,一般可執(zhí)行多次。
timer定時器表示在一段時間后執(zhí)行,默認(rèn)情況下只執(zhí)行一次,如果想再次執(zhí)行的話,每次都需要調(diào)用 time.Reset() 方法,此時效果類似ticker定時器。同時也可以調(diào)用 Stop() 方法取消定時器
timer定時器比ticker定時器多一個 Reset() 方法,兩者都有 Stop() 方法,表示停止定時器,底層都調(diào)用了stopTimer() 函數(shù)。
除了上面的定時器外,Go 里的 time.Sleep 也起到了類似一次性使用的定時功能。只不過 time.Sleep 使用了系統(tǒng)調(diào)用。而像上面的定時器更多的是靠 Go 的調(diào)度行為來實現(xiàn)。
無論哪種計時器,.C 都是一個 chan Time 類型且容量為 1 的單向 Channel,當(dāng)有超過 1 個數(shù)據(jù)的時候便會被阻塞,以此保證不會被觸發(fā)多次。
到此這篇關(guān)于Golang定時器Timer與Ticker的使用詳解的文章就介紹到這了,更多相關(guān)Golang定時器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺析golang?github.com/spf13/cast?庫識別不了自定義數(shù)據(jù)類型
這篇文章主要介紹了golang?github.com/spf13/cast庫識別不了自定義數(shù)據(jù)類型,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08golang進(jìn)行簡單權(quán)限認(rèn)證的實現(xiàn)
本文主要介紹了golang簡單權(quán)限認(rèn)證的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09HTTP服務(wù)壓力測試工具及相關(guān)術(shù)語講解
這篇文章主要為大家介紹了HTTP服務(wù)壓力測試工具使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04go語言實現(xiàn)簡易比特幣系統(tǒng)之交易簽名及校驗功能
這篇文章主要介紹了go語言實現(xiàn)簡易比特幣系統(tǒng)之交易簽名及校驗功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-04-04