Ticker资源泄露

问题

项目中遇到的一个问题,项目运行一段时间后CPU使用率会逐步上升到100%,导致系统异常,排查是在Ticker上使用出现错误,网上也有类似的案例:在使用Tikcer后没有释放导致。在此做下记录。

引用一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func (p *Pinger) run() {
timeout := time.NewTicker(p.Timeout) // 创建Ticker timeout
interval := time.NewTicker(p.Interval) // 创建Ticker
for {
select {
case <-p.done: // 正常退出,未关闭Ticker
wg.Wait()
return
case <-timeout.C: // 超时退出,未关闭Ticker
close(p.done)
wg.Wait()
return
case <-interval.C:
if p.Count > 0 && p.PacketsSent >= p.Count {
continue
}
err = p.sendICMP(conn)
if err != nil {
fmt.Println("FATAL: ", err.Error())
}
case r := <-recv:
err := p.processPacket(r)
if err != nil {
fmt.Println("FATAL: ", err.Error())
}
}
if p.Count > 0 && p.PacketsRecv >= p.Count { // 退出,未关闭Ticker
close(p.done)
wg.Wait()
return
}
}
}

该段代码可以看出,这个函数是有出口的,但在出口处没有关闭Ticker,导致资源泄露。这个问题已经被修复了,可以看到修复后的局部代码如下:

1
2
3
4
timeout := time.NewTicker(p.Timeout)
defer timeout.Stop() // 使用defer保证Ticker最后被关闭
interval := time.NewTicker(p.Interval)
defer interval.Stop() // 使用defer保证Ticker最后被关闭

总结

有一种情况使用Ticker不主动关闭也不会造成资源泄露,比如,函数创建Ticker后就不会退出,直到进程结束。这种情况下不会持续的创建Ticker,也就不会造成资源泄露。

但是,不管哪种情况,创建一个Ticker后,紧跟着使用defer语句关闭Ticker总是好的习惯。因为,有可能别人无意间拷贝了你的部分代码,而忽略了关闭Ticker的动作。