Golang當(dāng)中的定時器實例詳解
前言
在平時寫代碼的時候,我們經(jīng)常會遇到在將來某個時間點或者間隔一段時間重復(fù)執(zhí)行函數(shù)。這個時候我們就可以考慮使用定時器。本片文章主要介紹一下golang當(dāng)中的幾個常用的定時器。time.Timer,time.Ticker,time.After以及time.AfterFunc和time.Ticker的基本使用
定時器的基本使用
golang當(dāng)中的定時器有這個一次性的定時器(Timer)和周期性的定時器(Ticker).在平時的編程當(dāng)中經(jīng)常會使用timer當(dāng)中的ticker,AfterFunc定時器,而NewTicker是每隔多長時間觸發(fā),NewTimer是等待多長時間觸發(fā)一次請注意是只觸發(fā)一次。請注意一下兩者的區(qū)別。
下面我們來首先來使用一下這兩個定時器首先是這個Timer定時器
package main import ( "fmt" "time" ) func main() { myTimer := time.NewTimer(time.Second * 3) //初始化定時器 var i = 0 for { select { case <-myTimer.C: i++ fmt.Printf("the counter is%d", i) myTimer.Reset(time.Second * 3) //注意需要重新設(shè)置 } } myTimer.Stop() //不在使用需要將其停止 }
注意這個timer定時器超時之后需要重新進(jìn)行設(shè)置,才能重新觸發(fā)。如果上面的代碼我們沒有Reset,那么就會導(dǎo)致死鎖。其實我們也可以看看這個Timer是怎么是實現(xiàn)的
func NewTimer(d Duration) *Timer { c := make(chan Time, 1) t := &Timer{ C: c, // 信道 r: runtimeTimer{ when: when(d), // 觸發(fā)時間 f: sendTime, // 時間到了之后的調(diào)用函數(shù) arg: c, // 調(diào)用sendTime時的入?yún)? }, } startTimer(&t.r) // 把定時器的r字段放入由定時器維護(hù)協(xié)程維護(hù)的堆中 return t }
從上面的構(gòu)造函數(shù)中可以大概看出定時器的工作流程,這里面最重要的是runtimeTimer。構(gòu)造定時器的時候會把runtimeTimer放入由定時器維護(hù)協(xié)程維護(hù)的堆中,當(dāng)時間到了之后,維護(hù)協(xié)程把r從堆中移除,并調(diào)用r的sendTime函數(shù),sendTime的入?yún)⑹嵌〞r器的信道C??梢酝茢啵瑂endTime中執(zhí)行的邏輯應(yīng)該是向信道C中推送時間,通知上游系統(tǒng)時間到了,而事實正是如此:
func sendTime(c interface{}, seq uintptr) { // Non-blocking send of time on c. // Used in NewTimer, it cannot block anyway (buffer). // Used in NewTicker, dropping sends on the floor is // the desired behavior when the reader gets behind, // because the sends are periodic. select { case c.(chan Time) <- Now(): //時間到了之后把當(dāng)前時間放入信道中 default: } }
其實這個time.After就是對這個time.Timer的一個封裝,所以如果我們上面使用這個time.After那么會頻繁的創(chuàng)建time.Timer對象
下面我們在來看一下這個time.AterFunc()定時器。
Golang當(dāng)中的AfterFunc函數(shù)用于等待經(jīng)過時間,此后在其自己的協(xié)程當(dāng)中調(diào)用定義的函數(shù)f.函數(shù)在時間包下定義。下面我們一起看看如何使用這個
import ( "fmt" "time" ) func main() { f := func() { fmt.Println("the func is call after 3 second") } myTime := time.AfterFunc(time.Second*3, f) defer myTime.Stop() //定時器不用了需要關(guān)閉 time.Sleep(time.Second * 4) }
下面我們在看看這個time.NewTicker定時器的使用
package main import ( "fmt" "time" ) func main() { mytick := time.NewTicker(time.Second * 2) defer mytick.Stop() //定時器不用了需要關(guān)閉 done := make(chan struct{}) go func() { for { time.Sleep(time.Second * 10) done <- struct{}{} } }() for { select { case <-done: fmt.Println("done!!!!") return case t := <-mytick.C: fmt.Printf("the curtime is %v\n", t) } } }
下面我們來看一下這個陷阱,這個需要注意
package main import ( "fmt" "time" ) func main() { var count int for { select { case <-time.Tick(time.Second * 1): fmt.Println("case1") count++ fmt.Println("count--->", count) case <-time.Tick(time.Second * 2): fmt.Println("case2") count++ fmt.Println("count--->", count) } } }
這個代碼是有陷阱的,下面我們來看看這個運行結(jié)果是什么?
可見 case2 永遠(yuǎn)沒有被執(zhí)行到,問題就出在代碼邏輯上,首先看time.Tick方法。我們可以看一下這個方法就知道
func Tick(d Duration) <-chan Time { if d <= 0 { return nil } return NewTicker(d).C }
它每次都會創(chuàng)建一個新的定時器,隨著 for 循環(huán)進(jìn)行, select 始終監(jiān)聽兩個新創(chuàng)建的定時器,老的定時器被拋棄掉了,也就不會去讀取老定時器中的通道。
select 可以同時監(jiān)聽多個通道,誰先到達(dá)就先讀取誰,如果同時有多個通道有消息到達(dá),那么會隨機讀取一個通道,其他的通道由于沒有被讀取,所以數(shù)據(jù)不會丟失,需要循環(huán)調(diào)用 select 來讀取剩下的通道。
總結(jié):
- tick創(chuàng)建完成之后,不是馬上有一個tick.第一個tick在你設(shè)置的多少秒之后才會進(jìn)行創(chuàng)建
- golang當(dāng)中的定時器實質(zhì)上是這個單項的管道
- time.NewTicker會定時觸發(fā)任務(wù),當(dāng)下一次執(zhí)行到來而當(dāng)前任務(wù)畫面執(zhí)行完,會等待當(dāng)前任務(wù)執(zhí)行完畢在進(jìn)行下一次任務(wù)。
- Ticker和Timer的不同之處是,Ticker時間到達(dá)之后不需要人為的調(diào)用Reset方法來重新設(shè)置時間
到此這篇關(guān)于Golang當(dāng)中的定時器的文章就介紹到這了,更多相關(guān)Golang定時器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決電腦用GoLand太卡將VsCode定制成Go IDE步驟過程
這篇文章主要為大家介紹了解決電腦用GoLand太卡,將VsCode定制成Go IDE步驟過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11golang?日志庫ZAP[uber-go?zap]示例詳解
ZAP是由Uber開源的高性能Go語言日志庫,支持多種日志級別及基本信息打印,雖然ZAP本身不支持日志分割,但可以結(jié)合lumberjack進(jìn)行日志切割,實現(xiàn)日志按文件大小、時間或間隔切割等功能,ZAP提供Logger和SugaredLogger兩種日志記錄器2024-10-10go 代碼的調(diào)試---打印調(diào)用堆棧的實例
下面小編就為大家?guī)硪黄猤o 代碼的調(diào)試---打印調(diào)用堆棧的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10