GO語言在time包中提供了三種定時器的使用方式:
1.第一種:ticker
// A Ticker holds a channel that delivers `ticks' of a clock // at intervals. type Ticker struct { C <-chan Time // The channel on which the ticks are delivered. r runtimeTimer }
通過 time.NewTicker() 創建,這種類型,ticker會不斷的按照設定的間隔時間觸發,除非主動終止運行。
2.第二種:timer
// The Timer type represents a single event. // When the Timer expires, the current time will be sent on C, // unless the Timer was created by AfterFunc. // A Timer must be created with NewTimer or AfterFunc. type Timer struct { C <-chan Time r runtimeTimer }
通過 time.NewTimer() 創建,這種類型,timer只會執行一次,當然,可以在執行完以后通過調用 timer.Reset() 讓定時器再次工作,並可以更改時間間隔。
3.第三種:After()
// After waits for the duration to elapse and then sends the current time // on the returned channel. // It is equivalent to NewTimer(d).C. // The underlying Timer is not recovered by the garbage collector // until the timer fires. If efficiency is a concern, use NewTimer // instead and call Timer.Stop if the timer is no longer needed. func After(d Duration) <-chan Time { return NewTimer(d).C }
從代碼可以看到,After()其實是Timer的一個語法糖。
下面通過代碼演示一下三種方式的使用:
1.Ticker
1 ticker := time.NewTicker(time.Second * 1) // 運行時長 2 ch := make(chan int) 3 go func() { 4 var x int 5 for x < 10 { 6 select { 7 case <-ticker.C: 8 x++ 9 fmt.Printf("%d\n", x) 10 } 11 } 12 ticker.Stop() 13 ch <- 0 14 }() 15 <-ch // 通過通道阻塞,讓任務可以執行完指定的次數。
該ticker每1秒觸發一次,即ticker.C中每一秒會有一個內容加入,最后通過向ch中寫入數字,讓程序解除阻塞,繼續執行。
2.Timer
1 timer := time.NewTimer(time.Second * 1) // timer 只能按時觸發一次,可通過Reset()重置后繼續觸發。 2 go func() { 3 var x int 4 for { 5 select { 6 case <-timer.C: 7 x++ 8 fmt.Printf("%d,%s\n", x, time.Now().Format("2006-01-02 15:04:05")) 9 if x < 10 { 10 timer.Reset(time.Second * 2) 11 } else { 12 ch <- x 13 } 14 } 15 } 16 }() 17 <-ch
3.After()
1 // 阻塞一下,等待主進程結束 2 tt := time.NewTimer(time.Second * 10) 3 <-tt.C 4 fmt.Println("over.") 5 6 <-time.After(time.Second * 4) 7 fmt.Println("再等待4秒退出。tt 沒有終止,打印出 over 后會看見在繼續執行...") 8 tt.Stop() 9 <-time.After(time.Second * 2) 10 fmt.Println("tt.Stop()后, tt 仍繼續執行,只是關閉了 tt.C 通道。")
4.我們可以利用這些基本的方法,設計自己的定時任務管理。
1 type jobFunc2 func(j *job) 2 3 type job struct { 4 jf jobFunc2 5 params map[string]interface{} 6 ch chan int 7 } 8 9 func NewJob() *job { 10 return &job{ 11 params: make(map[string]interface{}), 12 ch: make(chan int), 13 } 14 } 15 16 func (j *job) Run(t time.Duration) { 17 ticker := time.NewTicker(time.Second * t) 18 go func() { 19 for { 20 select { 21 case <-ticker.C: 22 j.jf(j) 23 case <-j.ch: 24 fmt.Println("收到結束指令") 25 ticker.Stop() 26 break 27 } 28 } 29 }() 30 31 } 32 33 func main() { 34 j := NewJob() 35 j.jf = func(jj *job) { 36 fmt.Println("定時任務執行...", time.Now().Format("15:04:05 2006-02-01"), jj.params) 37 } 38 j.params["p1"] = "第一個參數" 39 j.params["p2"] = 100 40 j.Run(1) 41 42 // 阻塞一下,等待主進程結束 43 tt := time.NewTimer(time.Second * 10) 44 <-tt.C 45 fmt.Println("over.") 46 47 <-time.After(time.Second * 4) 48 fmt.Println("再等待4秒退出。tt 沒有終止,打印出 over 后會看見在繼續執行...") 49 tt.Stop() 50 <-time.After(time.Second * 2) 51 fmt.Println("tt.Stop()后, tt 仍繼續執行,只是關閉了 tt.C 通道。") 52 }
部分執行結果截圖:
最后補充一下,通過channel去終止任務的執行。
1 // 阻塞一下,等待主進程結束 2 tt := time.NewTimer(time.Second * 10) 3 <-tt.C 4 fmt.Println("over.") 5 6 <-time.After(time.Second * 4) 7 fmt.Println("再等待4秒退出。tt 沒有終止,打印出 over 后會看見在繼續執行...") 8 tt.Stop() 9 <-time.After(time.Second * 2) 10 fmt.Println("tt.Stop()后, tt 仍繼續執行,只是關閉了 tt.C 通道。") 11 j.ch <- 0 12 <-time.After(time.Second * 2) 13 fmt.Println("又等了2秒鍾...這兩秒鍾可以看到 tt 沒干活了...")
在GO語言編寫中,要熟練使用 channel。