我有一个从通道读取(间接从套接字接收)的循环,但如果没有其他流量,还必须定期发送 ping。所以我在每次循环迭代时创建一个
time.Timer
,如下所示:
var timer *time.Timer
for {
timer = time.NewTimer(pingFrequency)
select {
case message := <-ch:
....
case <-timer.C:
ping()
}
_ = timer.Stop()
}
Stop
方法的 doco 意味着如果它返回 false,您应该排空通道,但这确实有必要,因为timer
的先前值不再使用并将由 GC 释放。即,即使旧计时器.C 有未读值也没关系,因为没有任何东西再使用该通道。
医生。对于
t.Stop()
说:
为了确保调用 Stop 后通道为空, 检查返回值并排空通道。
我的问题是:为什么需要确保通道是空的,因为没有任何东西在使用它并且它将被 GC 释放?
请注意,我知道在一般情况下停止计时器的正确方法。
if !timer.Stop() {
select {
case <-timer.C:
default:
}
}
这个具体问题尚未在 SO 或其他地方得到解答(除了下面@JimB 的评论)。
在计时器触发并且 Goroutine 向通道缓冲区发送消息后,计时器确实会被 GC 自动释放。但这确实意味着 goroutine 将占用内存,直到计时器触发为止。
有时这没什么问题,但如果你有很多这样的计时器,它就会变得很重要。例如,如果您在 HTTP 请求处理程序中生成计时器,则可能会遇到这样的情况:相对于您处理请求的速率,计时器完成速度不够快,并且服务器最终将耗尽内存和崩溃。
如果您正在编写库代码,那么清理计时器尤其重要,因为您不知道人们将如何使用您的库以及内存使用情况是否会对他们构成重大问题。