這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
程式設計目標是在程式啟動10秒後執行某個任務,例如日誌轉儲(rotate),以後每隔15秒執行一次。
初次的設計
package mainimport ( "time" "fmt")func main() { timer := time.NewTimer(10 * time.Second) fmt.Println(time.Now()) for { select { case <-timer.C: fmt.Println(time.Now()) time.Sleep(1 * time.Second) timer.Reset(15 * time.Second) } }}
顯然,在設定下一個定時(timer.Reset(15 * time.Second))之前,多少都會消耗一點時間,即使是毫秒級的延遲,也一定會產生累計誤差。
為了使問題更明顯,在程式中增加了一秒的 Sleep。測試結果如下:
2017-07-18 23:16:24.791623 +0800 CST2017-07-18 23:16:34.7917567 +0800 CST2017-07-18 23:16:50.7920782 +0800 CST2017-07-18 23:17:06.7929373 +0800 CST2017-07-18 23:17:22.7944063 +0800 CST2017-07-18 23:17:38.7951302 +0800 CST2017-07-18 23:17:54.7968096 +0800 CST2017-07-18 23:18:10.7985468 +0800 CST2017-07-18 23:18:26.7993588 +0800 CST2017-07-18 23:18:42.7996568 +0800 CST2017-07-18 23:18:58.8008621 +0800 CST
改進後的程式
package mainimport ( "time" "fmt")func main() { next := time.Now().Add(10 * time.Second) timer := time.NewTimer(next.Sub(time.Now())) fmt.Println(time.Now()) for { select { case <-timer.C: fmt.Println(time.Now()) time.Sleep(1 * time.Second) next = next.Add(15 * time.Second) timer.Reset(next.Sub(time.Now())) } }}
簡單說就是增加了一個時間變數 next,在設定定時器之前,通過計算獲得下一次任務的執行時間。
而定時器用 next - Now() 來設定。測試結果如下:
2017-07-18 23:16:20.2456695 +0800 CST2017-07-18 23:16:30.2466397 +0800 CST2017-07-18 23:16:45.2457191 +0800 CST2017-07-18 23:17:00.2458328 +0800 CST2017-07-18 23:17:15.2451861 +0800 CST2017-07-18 23:17:30.2452624 +0800 CST2017-07-18 23:17:45.2468138 +0800 CST2017-07-18 23:18:00.245947 +0800 CST
從以上結果看,精度可控制在 ± 2ms。完全可以滿足定時轉儲記錄檔的需要。