1. time.Newtimer是可以在没有强引用的时候被gc回收掉的。但是time.NewTicker必须在defer中使用stop来释放资源,否则资源永远不会被gc回收
2. time.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.
这种只能用于no need to shut down的情况,因此一般是不应该使用这个的
相关区别:
1. timer是用于只执行一次延时获取能力的情况。如果要多次使用,需要结合reset。例如:
func TestCs2(t *testing.T) { timer := time.NewTimer(2 * time.Second) for { select { case t := <-timer.C: timer.Reset(2 * time.Second) fmt.Printf("time:%v ", t) } } }
2. timer的注释中明确说明了如果使用reset的正确用法。
func TestBf(t *testing.T) { fmt.Printf("time:%v ", time.Now()) timer := time.NewTimer(time.Second) time.Sleep(2 * time.Second) if !timer.Stop() { <-timer.C } timer.Reset(5 * time.Second) for { x := <-timer.C fmt.Printf("time:%v ", x) } }
step1 没有进行if !timer.Stop()是因为reset的值和创建时的值都是2s。因此无需reset。
其他变更了reset值的情况,必须进行if !timer.Stop()的判断。
3. time.ticker是重复进行定时触发的。和time.timer一样,其实底层是从一个1个值的channel中获取定时触发的time值。问题在于,time.ticker的stop方法是没有返回bool值的。因此没有办法通过timer类似的
if !timer.Stop() { <-timer.C }
这种方式,将已经放入到channel中的数据取出。
因此
func TestCs(t *testing.T) { fmt.Printf("time:%v ", time.Now()) timer := time.NewTicker(time.Second) defer timer.stop() time.Sleep(2 * time.Second) go func() { for { select { case x := <-timer.C: fmt.Printf("time:%v ", x) } } }() timer.Reset(5 * time.Second) time.Sleep(time.Hour) }
类似以上代码,会打印出如下结果
time:2024-01-21 21:04:24.857927 +0800 +08 m=+0.011576126
time:2024-01-21 21:04:25.858828 +0800 +08 m=+1.012470209
time:2024-01-21 21:04:31.8597 +0800 +08 m=+7.013304418
time:2024-01-21 21:04:36.859213 +0800 +08 m=+12.012786126
第二次21:04:25,就是没有取出已经放入在channl中的数据导致的。
同样注意,即使是timer。不先进行if !timer.Stop() { <-timer.C },而是直接操作timer.Reset(5 * time.Second)也是可以的,只不过会和ticker的情况一样,如果是channel中已经触发过了的数据,没有被取出,会在取出的时候,第一次间隔还是reset前的时间间隔。
time.After的情况和以上的情况类似,可以参见这篇文章
Go坑:time.After可能导致的内存泄露问题分析