golang 一次性定時(shí)器Timer用法及實(shí)現(xiàn)原理詳解
前言
定時(shí)器在Go語(yǔ)言應(yīng)用中使用非常廣泛,Go語(yǔ)言的標(biāo)準(zhǔn)庫(kù)里提供兩種類型的計(jì)時(shí)器,一種是一次性的定時(shí)器Timer
,另外一種是周期性的定時(shí)器Ticker
。本文主要來看一下Timer
的用法和實(shí)現(xiàn)原理,需要的朋友可以參考以下內(nèi)容,希望對(duì)大家有幫助。
Timer
Timer
是一種單一事件的定時(shí)器,即經(jīng)過指定的時(shí)間后觸發(fā)一個(gè)事件,因?yàn)?code>Timer只執(zhí)行一次就結(jié)束,所以稱為單一事件,這個(gè)事件通過其本身提供的channel
進(jìn)行通知觸發(fā)。
timer結(jié)構(gòu)體
通過src/time.sleep.go:Timer
定義了Timer
數(shù)據(jù)結(jié)構(gòu):
// Timer代表一次定時(shí),時(shí)間到達(dá)后僅執(zhí)行一個(gè)事件。 type Timer struct { C <-chan Time r runtimeTimer }
它提供了一個(gè)channel
,在定時(shí)時(shí)間到達(dá)之前,沒有數(shù)據(jù)寫入timer.C
會(huì)一直阻塞,直到時(shí)間到達(dá),向channel
寫入系統(tǒng)時(shí)間,阻塞解除,可以從中讀取數(shù)據(jù),這就是一個(gè)事件。
創(chuàng)建定時(shí)器
func NewTimer(d Duration) *Timer
通過上面方法指定一個(gè)事件即可創(chuàng)建一個(gè)Timer,Timer一經(jīng)創(chuàng)建便開始計(jì)時(shí),不需要額外的啟動(dòng)命令。
示例:
func main() { timer := time.NewTimer(time.Second * 5) //設(shè)置超時(shí)時(shí)間5s <- timer.C fmt.Println("Time out!") }
停止定時(shí)器
Timer創(chuàng)建后可以隨時(shí)停止,停止計(jì)時(shí)器的方法如下:
func (t *Timer) Stop() bool
其返回值代表定時(shí)器有沒有超時(shí):
- true:定時(shí)器超時(shí)前停止,后續(xù)不會(huì)再有事件發(fā)送。
- false:定時(shí)器超時(shí)后停止。
示例:
func main() { timer := time.NewTimer(time.Second * 5) //設(shè)置超時(shí)時(shí)間5s timer.Stop() }
重置定時(shí)器
已經(jīng)過期的定時(shí)器或者已停止的定時(shí)器,可以通過重置動(dòng)作重新激活,方法如下:
func (t *Timer) Reset(d Duration) bool
重置的動(dòng)作本質(zhì)上是先停掉定時(shí)器,再啟動(dòng),其返回值也即是停掉計(jì)時(shí)器的返回值。
func main() { timer := time.NewTimer(time.Second * 5) <- timer.C fmt.Println("Time out!") timer.Stop() timer.Reset(time.Second*3) // 重置定時(shí)器 }
實(shí)現(xiàn)原理
每個(gè)Go應(yīng)用程序都有一個(gè)協(xié)程專門負(fù)責(zé)管理所有的Timer,這個(gè)協(xié)程負(fù)責(zé)監(jiān)控Timer是否過期,過期后執(zhí)行一個(gè)預(yù)定義的動(dòng)作,這個(gè)動(dòng)作對(duì)于Timer而言就是發(fā)送當(dāng)前時(shí)間到管道中。
數(shù)據(jù)結(jié)構(gòu)
type Timer struct { C <-chan Time r runtimeTimer }
Timer只有兩個(gè)成員:
- C:channel,上層應(yīng)用根據(jù)此管道接收事件;
- r:runtimeTimer定時(shí)器,該定時(shí)器即系統(tǒng)管理的定時(shí)器,上層應(yīng)用不可見。
runtimeTimer
任務(wù)的載體,用于監(jiān)控定時(shí)任務(wù),每創(chuàng)建一個(gè)Timer就創(chuàng)建一個(gè)runtimeTimer變量,然后把它交給系統(tǒng)進(jìn)行監(jiān)控,我們通過設(shè)置runtimeTimer過期后的行為來達(dá)到定時(shí)的目的。
源碼包src/time/sleep.go:runtimeTimer定義了其數(shù)據(jù)結(jié)構(gòu):
type runtimeTimer struct { tb uintptr // 存儲(chǔ)當(dāng)前定時(shí)器的數(shù)組地址 i int // 存儲(chǔ)當(dāng)前定時(shí)器的數(shù)組下標(biāo) when int64 // 當(dāng)前定時(shí)器觸發(fā)時(shí)間 period int64 // 當(dāng)前定時(shí)器周期觸發(fā)間隔 f func(interface{}, uintptr) // 定時(shí)器觸發(fā)時(shí)執(zhí)行的函數(shù) arg interface{} // 定時(shí)器觸發(fā)時(shí)執(zhí)行函數(shù)傳遞的參數(shù)一 seq uintptr // 定時(shí)器觸發(fā)時(shí)執(zhí)行函數(shù)傳遞的參數(shù)二(該參數(shù)只在網(wǎng)絡(luò)收發(fā)場(chǎng)景下使用) }
創(chuàng)建Timer
源碼實(shí)現(xiàn):
func NewTimer(d Duration) *Timer { c := make(chan Time, 1) // 創(chuàng)建一個(gè)管道 t := &Timer{ // 構(gòu)造Timer數(shù)據(jù)結(jié)構(gòu) C: c, // 新創(chuàng)建的管道 r: runtimeTimer{ when: when(d), // 觸發(fā)時(shí)間 f: sendTime, // 觸發(fā)后執(zhí)行函數(shù)sendTime arg: c, // 觸發(fā)后執(zhí)行函數(shù)sendTime時(shí)附帶的參數(shù) }, } startTimer(&t.r) // 此處啟動(dòng)定時(shí)器,只是把runtimeTimer放到系統(tǒng)協(xié)程的堆中,由系統(tǒng)協(xié)程維護(hù) return t }
NewTimer()
只是構(gòu)造了一個(gè)Timer
,然后把Timer.r
通過startTimer()
交給系統(tǒng)協(xié)程維護(hù)。- C 是一個(gè)帶1個(gè)容量的chan,這樣做有什么好處呢,原因是chan 無緩沖發(fā)送數(shù)據(jù)就會(huì)阻塞,阻塞系統(tǒng)協(xié)程,這顯然是不行的。
- 回調(diào)函數(shù)設(shè)置為
sendTime
,執(zhí)行參數(shù)為channel
,sendTime
就是到點(diǎn)往C 里面發(fā)送當(dāng)前時(shí)間的函數(shù)
sendTime實(shí)現(xiàn):
//c interface{} 就是NewTimer 賦值的參數(shù),就是channel func sendTime(c interface{}, seq uintptr) { select { case c.(chan Time) <- Now(): //寫不進(jìn)去的話,C 已滿,走default 分支 default: } }
停止Timer
停止Timer,就是把Timer從系統(tǒng)協(xié)程中移除。函數(shù)主要實(shí)現(xiàn)如下:
func (t *Timer) Stop() bool { return stopTimer(&t.r) }
stopTimer()即通知系統(tǒng)協(xié)程把該Timer移除,即不再監(jiān)控。系統(tǒng)協(xié)程只是移除Timer并不會(huì)關(guān)閉管道,以避免用戶協(xié)程讀取錯(cuò)誤。
重置Timer
重置Timer時(shí)會(huì)先把timer從系統(tǒng)協(xié)程中刪除,修改新的時(shí)間后重新添加到系統(tǒng)協(xié)程中。
func (t *Timer) Reset(d Duration) bool { w := when(d) active := stopTimer(&t.r) t.r.when = w startTimer(&t.r) return active }
以上就是golang 一次性定時(shí)器Timer用法及實(shí)現(xiàn)原理詳解的詳細(xì)內(nèi)容,更多關(guān)于go 一次性定時(shí)器Timer的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go中g(shù)routine通信與context控制實(shí)例詳解
隨著context包的引入,標(biāo)準(zhǔn)庫(kù)中很多接口因此加上了context參數(shù),下面這篇文章主要給大家介紹了關(guān)于Go中g(shù)routine通信與context控制的相關(guān)資料,需要的朋友可以參考下2022-02-02golang常用庫(kù)之配置文件解析庫(kù)-viper使用詳解
viper 配置管理解析庫(kù),是由大神 Steve Francia 開發(fā),他在google領(lǐng)導(dǎo)著 golang 的產(chǎn)品開發(fā),他也是 gohugo.io 的創(chuàng)始人之一,命令行解析庫(kù) cobra 開發(fā)者,這篇文章主要介紹了golang常用庫(kù)之配置文件解析庫(kù)-viper使用詳解,需要的朋友可以參考下2020-10-10Go語(yǔ)言中零拷貝的原理與實(shí)現(xiàn)詳解
零拷貝是相對(duì)于用戶態(tài)來講的,即數(shù)據(jù)在用戶態(tài)不發(fā)生任何拷貝,那么零拷貝的原理是什么,又是如何實(shí)現(xiàn)的呢,下面小編就來和大家詳細(xì)聊聊吧2023-08-08golang 項(xiàng)目打包部署環(huán)境變量設(shè)置方法
最近將 golang 項(xiàng)目打包部署在不同環(huán)境,下面分享一下我的心得體會(huì),對(duì)golang 項(xiàng)目打包部署環(huán)境變量設(shè)置方法感興趣的朋友一起看看吧2024-07-07golang使用aes庫(kù)實(shí)現(xiàn)加解密操作
這篇文章主要介紹了golang使用aes庫(kù)實(shí)現(xiàn)加解密操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-12-12