Go之定时器的使用
一、背景介绍
笔者最近在使用Go的定时器,发现Go提供的time包里面,按照下面几种场景做了区分,并分别提供一些API支持。主要场景如下所示:
1.超时一次之后,就不再使用的定时器,time.After()。2.每隔一段时间,就需要使用一次的定时器,time.Tick()。3.阻塞住goroutinue的定时器,time.Sleep(),准确来说这个不算一个定时器4.可以自由控制定时器启动和关闭的定时器,time.Ticker()。
二、实例演练
1. time.After()
调用time.After(duration),此函数马上返回,返回一个time.Time类型的Chan,不阻塞。后面你该做什么做什么,不影响。到了duration时间后,自动塞一个当前时间进去,底层是用NewTimer实现的。
参考:
https://golang.org/pkg/time/#After
func After(d Duration) <-chan Time
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.
例子:
代码:package mainimport ( 'fmt' 'time')func main() { tchan := time.After(time.Second * 3) fmt.Println(time.Now().String(),'tchan type:%T', tchan) fmt.Println(time.Now().String(),'mark 1') fmt.Println(time.Now().String(),'tchan=', <-tchan) // channel里取出数据之后,会发现超时间是3秒 fmt.Println(time.Now().String(),'mark 2')}输出结果:2009-11-10 23:00:00 +0000 UTC m=+0.000000001 tchan type:%T 0xc0001041202009-11-10 23:00:00 +0000 UTC m=+0.000000001 mark 12009-11-10 23:00:00 +0000 UTC m=+0.000000001 tchan= 2009-11-10 23:00:03 +0000 UTC m=+3.0000000012009-11-10 23:00:03 +0000 UTC m=+3.000000001 mark 2
场景讲解:
使用了time.After之后,只有在调用了<-tchan之后,才会真正的触发超时的操作,在此之前的代码是可以被立马执行的,因为time.After不会阻塞当前的goroutine。
2. time.Tick()
Tick函数是使用channel阻塞当前协程,完成定时任务的执行,用来处理间隔一段固定时间来处理一部分事情的。原文如下:
https://golang.org/pkg/time/#Tick
func Tick(d Duration) <-chan Time
Tick is a convenience wrapper for NewTicker providing access to the ticking channel only. While Tick is useful for clients that have no need to shut down the Ticker, be aware that without a way to shut it down the underlying Ticker cannot be recovered by the garbage collector; it 'leaks'. Unlike NewTicker, Tick will return nil if d <= 0.
例子:
代码:package mainimport ( 'fmt' 'time')func main() { c := time.Tick(2 * time.Second) for next := range c { fmt.Printf('%v \n', next) }}结果输出:timeout running program2009-11-10 23:00:02 +0000 UTC m=+2.000000001 2009-11-10 23:00:04 +0000 UTC m=+4.000000001 2009-11-10 23:00:06 +0000 UTC m=+6.000000001 2009-11-10 23:00:08 +0000 UTC m=+8.000000001
场景讲解:
这段代码是希望每次间隔2秒钟执行一次打印操作,备注:对于Tick来说,返回的是一个只读channel,这里的channel不要使用者做额外的操作,只需要使用就可以,因为这个Tick针对的场景就是那种不需要关闭时间的场景。
3. time.Sleep()
Sleep是使用睡眠完成定时,结束后继续往下执行循环来实现定时任务,Sleep的过程中是会阻塞当前goroutinue的。原文参考:
https://golang.org/pkg/time/#Sleep
func Sleep(d Duration)
Sleep pauses the current goroutine for at least the duration d. A negative or zero duration causes Sleep to return immediately.
例子:
代码:package mainimport ( 'fmt' 'time')func main() { for { fmt.Printf('start %v \n', time.Now()) time.Sleep(2 * time.Second) // time.Sleep的过程中,下面的打印不会执行 fmt.Printf('end %v \n', time.Now()) }}结果输出:timeout running programstart 2009-11-10 23:00:00 +0000 UTC m=+0.000000001 end 2009-11-10 23:00:02 +0000 UTC m=+2.000000001 start 2009-11-10 23:00:02 +0000 UTC m=+2.000000001 end 2009-11-10 23:00:04 +0000 UTC m=+4.000000001 start 2009-11-10 23:00:04 +0000 UTC m=+4.000000001 end 2009-11-10 23:00:06 +0000 UTC m=+6.000000001 start 2009-11-10 23:00:06 +0000 UTC m=+6.000000001 end 2009-11-10 23:00:08 +0000 UTC m=+8.000000001 .......
场景讲解:
从执行的打印来看,Sleep被调用之后,当前的goroutine是被阻塞住的,只有Sleep的时间到了之后,才会继续执行后续的操作。
4.time.Ticker
Ticker 和 Timer 类似,区别是:Ticker 中的 runtimeTimer 字段的 period 字段会赋值为 NewTicker(d Duration) 中的 d,表示每间隔 d 纳秒,定时器就会触发一次。
除非程序终止前定时器一直需要触发,否则,不需要时应该调用 Ticker.Stop 来释放相关资源。
原文参考如下:
https://golang.org/pkg/time/#Ticker
例子:
代码:package mainimport ( 'fmt' 'time')func main() { ticker := time.NewTicker(time.Second) defer ticker.Stop() done := make(chan bool) go func() { time.Sleep(5 * time.Second) // 5s后触发time.Stop,关闭ticker done <- true }() for { select { case <-done: fmt.Println('Done!') return case t := <-ticker.C: fmt.Println('Current time: ', t) } }}执行结果:Current time: 2009-11-10 23:00:01 +0000 UTC m=+1.000000001Current time: 2009-11-10 23:00:02 +0000 UTC m=+2.000000001Current time: 2009-11-10 23:00:03 +0000 UTC m=+3.000000001Current time: 2009-11-10 23:00:04 +0000 UTC m=+4.000000001Current time: 2009-11-10 23:00:05 +0000 UTC m=+5.000000001Done!
场景讲解:希望每隔1s执行一次打印操作,等待5s之后,终止这个操作。通过上面的输出我们能够看出来,time.Stop在调用了之后,timer便不再继续执行了。