您好,登錄后才能下訂單哦!
本篇內容介紹了“golang定時器Timer的用法和實現原理是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
Timer
是一種單一事件的定時器,即經過指定的時間后觸發一個事件,因為Timer
只執行一次就結束,所以稱為單一事件,這個事件通過其本身提供的channel
進行通知觸發。
通過src/time.sleep.go:Timer
定義了Timer
數據結構:
// Timer代表一次定時,時間到達后僅執行一個事件。 type Timer struct { C <-chan Time r runtimeTimer }
它提供了一個channel
,在定時時間到達之前,沒有數據寫入timer.C
會一直阻塞,直到時間到達,向channel
寫入系統時間,阻塞解除,可以從中讀取數據,這就是一個事件。
func NewTimer(d Duration) *Timer
通過上面方法指定一個事件即可創建一個Timer,Timer一經創建便開始計時,不需要額外的啟動命令。
示例:
func main() { timer := time.NewTimer(time.Second * 5) //設置超時時間5s <- timer.C fmt.Println("Time out!") }
Timer創建后可以隨時停止,停止計時器的方法如下:
func (t *Timer) Stop() bool
其返回值代表定時器有沒有超時:
true:定時器超時前停止,后續不會再有事件發送。
false:定時器超時后停止。
示例:
func main() { timer := time.NewTimer(time.Second * 5) //設置超時時間5s timer.Stop() }
已經過期的定時器或者已停止的定時器,可以通過重置動作重新激活,方法如下:
func (t *Timer) Reset(d Duration) bool
重置的動作本質上是先停掉定時器,再啟動,其返回值也即是停掉計時器的返回值。
func main() { timer := time.NewTimer(time.Second * 5) <- timer.C fmt.Println("Time out!") timer.Stop() timer.Reset(time.Second*3) // 重置定時器 }
每個Go應用程序都有一個協程專門負責管理所有的Timer,這個協程負責監控Timer是否過期,過期后執行一個預定義的動作,這個動作對于Timer而言就是發送當前時間到管道中。
type Timer struct { C <-chan Time r runtimeTimer }
Timer只有兩個成員:
C:channel,上層應用根據此管道接收事件;
r:runtimeTimer定時器,該定時器即系統管理的定時器,上層應用不可見。
任務的載體,用于監控定時任務,每創建一個Timer就創建一個runtimeTimer變量,然后把它交給系統進行監控,我們通過設置runtimeTimer過期后的行為來達到定時的目的。
源碼包src/time/sleep.go:runtimeTimer定義了其數據結構:
type runtimeTimer struct { tb uintptr // 存儲當前定時器的數組地址 i int // 存儲當前定時器的數組下標 when int64 // 當前定時器觸發時間 period int64 // 當前定時器周期觸發間隔 f func(interface{}, uintptr) // 定時器觸發時執行的函數 arg interface{} // 定時器觸發時執行函數傳遞的參數一 seq uintptr // 定時器觸發時執行函數傳遞的參數二(該參數只在網絡收發場景下使用) }
源碼實現:
func NewTimer(d Duration) *Timer { c := make(chan Time, 1) // 創建一個管道 t := &Timer{ // 構造Timer數據結構 C: c, // 新創建的管道 r: runtimeTimer{ when: when(d), // 觸發時間 f: sendTime, // 觸發后執行函數sendTime arg: c, // 觸發后執行函數sendTime時附帶的參數 }, } startTimer(&t.r) // 此處啟動定時器,只是把runtimeTimer放到系統協程的堆中,由系統協程維護 return t }
NewTimer()
只是構造了一個Timer
,然后把Timer.r
通過startTimer()
交給系統協程維護。
C 是一個帶1個容量的chan,這樣做有什么好處呢,原因是chan 無緩沖發送數據就會阻塞,阻塞系統協程,這顯然是不行的。
回調函數設置為sendTime
,執行參數為channel
,sendTime
就是到點往C 里面發送當前時間的函數
sendTime實現:
//c interface{} 就是NewTimer 賦值的參數,就是channel func sendTime(c interface{}, seq uintptr) { select { case c.(chan Time) <- Now(): //寫不進去的話,C 已滿,走default 分支 default: } }
停止Timer,就是把Timer從系統協程中移除。函數主要實現如下:
func (t *Timer) Stop() bool { return stopTimer(&t.r) }
stopTimer()即通知系統協程把該Timer移除,即不再監控。系統協程只是移除Timer并不會關閉管道,以避免用戶協程讀取錯誤。
重置Timer時會先把timer從系統協程中刪除,修改新的時間后重新添加到系統協程中。
func (t *Timer) Reset(d Duration) bool { w := when(d) active := stopTimer(&t.r) t.r.when = w startTimer(&t.r) return active }
time包下有一個Ticker結構體
// Ticker保管一個通道,并每隔一段時間向其傳遞"tick"。 type Ticker struct { C <-chan Time // 周期性傳遞時間信息的通道. r runtimeTimer }
func NewTicker(d Duration) *Ticker{}
NewTicker返回一個新的Ticker,該Ticker包含一個通道字段,并會每隔時間段d,就向該通道發送當時的時間。它會調整時間間隔或者丟棄tick信息以適應反應慢的接收者。如果d<=0會panic。關閉該Ticker可以釋放相關資源。
func (t *Ticker) Stop()
Stop關閉一個Ticker。在關閉后,將不會發送更多的tick信息。Stop不會關閉通道t.C,以避免從該通道的讀取不正確的成功。
例子
package main import ( "fmt" "time" ) func main() { t := time.NewTicker(5 * time.Second) //創建定時器 defer t.Stop() go sync(t) select { } } func sync(t *time.Ticker) { for { // 每5秒中從chan t.C 中讀取一次 <-t.C fmt.Println("執行數據備份任務:", time.Now().Format("2006-01-02 15:04:05")) } }
“golang定時器Timer的用法和實現原理是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。