Go定時器的三種實現方式

碼農談IT發表於2022-12-21

很多時候需要週期性的執行某些操作,就需要用到定時器。定時器有三種思路。

Sleep

使用休眠,讓當前Goroutine休眠一定的時間來實現定時的效果,缺點是程式執行速度不均勻,導致定時週期不均勻。

for{
  fmt.Println(time.Now())
  time.Sleep(time.Second*1)
 }

Timer

Go 語言的內建包,指定一個時間開始計時,時間到之後會向外傳送通知,傳送通知的方式就是使用<-chan Time 返回內容。

第一種方式,直接在需要等待處使用,效果和Sleep一樣,一使用就卡在那了內部就是使用了Timer

 fmt.Println(time.Now())
 <-time.After(1*time.Second)
 fmt.Println(time.Now())

也可以把他拆分開,在任意地方進行等待

 timer := time.NewTimer(1 * time.Second)
 <-timer.C
 fmt.Println(time.Now())

但是以上只是做到延遲一次性執行,我們來改造一下,把他變成定時器。

    done := make(chan struct{})
 timer := time.NewTimer(1 * time.Second)
 go func() {
  for {
   select {
   case <-timer.C:
    fmt.Println(time.Now())
    timer.Reset(1 * time.Second)
   case <-done:
    return
   }
  }
 }()
 <-time.After(5*time.Second + time.Millisecond*100)
 done <- struct{}{}
  • 定義子Goroutine的目的是為了防止形成死鎖,讓定時器最終能退出,在實際專案中可能需要一個永久執行的定時器,一般為了不影響專案主邏輯也會這樣定義。如果你的專案就是定時任務,我建議也這麼寫,這樣可以註冊很多個定時器互不影響。
  • done是為了判斷執行是否結束,防止主Goroutine提前退出。
  • 這個示例只有兩個case,實戰中如果有加其他case需要給每個case內都做一次Reset,保證重置定時器。

Ticker

相比上述使用延遲執行功能實現的定時器,Ticker 本身就是一個定時器(內部封裝了Timer),我們使用起來就非常簡單。

ticker := time.NewTicker(1 * time.Second)
 go func() {
  for {
   <-ticker.C
   fmt.Println(time.Now())
  }
 }()
 <-time.After(5 * time.Second + time.Millisecond*100)
 ticker.Stop()

在select 一節中講述的官方超時控制方案非常的實用,也是使用的此函式。還使用到timer.Stoptimer.Reset這兩個內建函式這裡就不展開講解了,建議進行復習。

小結

定時器一般用來週期性執行任務,比如定時同步資料、計算報表、傳送通知。

  • time.Sleep 使用休眠,讓當前goroutine休眠一定的時間來實現定時的效果,缺點是內部邏輯執行的速度會影響到定時器的時間差,無法做到精確間隔。
  • Timer 類似於Sleep的延遲處理,透過channel來獲得通知,也可以改造成定時器。因為是延遲處理,所以要記得重置時間來實現定時執行的效果。
  • Ticker 現成的定時器,內部也是封裝了 Timer

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024924/viewspace-2928847/,如需轉載,請註明出處,否則將追究法律責任。

相關文章