我在goroutine中select
打开了一个 idle timeout 计时器,如果我看到要取消该计时器的活动。
我看过the documentation,但我不太肯定我对它说的是什么。
func (t *Timer) Stop() bool
停止可防止定时器触发。如果调用停止计时器,则返回true;如果计时器已过期或已停止,则返回false。停止并不会关闭通道,以防止从通道读取失败。为防止使用NewTimer创建的计时器在调用Stop之后触发,请检查返回值并清空通道。例如,假设尚未从t.C收到该程序:
if !t.Stop() { <-t.C }
不能与计时器通道的其他接收同时进行。
我试图了解何时必须手动排干通道。
我会列出我的理解,如果我错了,请纠正我。
如果Stop
返回了false
,则意味着:
对于我来说,从计时器中获取多余的事件没什么大不了的,这是否表明我应该在这里做什么?
答案 0 :(得分:4)
您可能需要耗尽通道的原因是因为goroutine的调度方式。
问题
想象一下这种情况:
t.C
发送一个值t.Stop()
。在这种情况下,通道t.C
上有一个值,并且t.Stop()
返回false是因为“计时器已过期”(即,它在t.C
上发送了值)。 / p>
文档说“无法与其他接收者同时完成”的原因是,不能保证if !t.Stop {
和<-t.C
之间的顺序。停止命令可能返回false,并输入if主体。然后可以安排另一个goroutine,并从t.C
中读取if语句主体试图消耗的值。这将导致数据争用并导致if语句内部的阻塞。 (正如您在问题中指出的那样!)
解决方案
这取决于监听计时器的事物的行为。
如果只是简单选择:
select {
case result <- doWork():
case <-t.C
}
类似上面的东西。可能发生的事情之一:
doWork
完成工作,一切顺利。doWork()
才能完成选择。t.Stop()
,但它也是如此
延迟,因为已发送值,导致超时,中断
选择。只要您对情况4满意,就无需在调用Stop.
后互动/耗尽频道
如果您对情况4不满意,则仍然无法排空t.C
频道,因为还有另一个goroutine在监听它。这可能会阻塞if语句。相反,您必须找到另一种布置代码的方法,或者确保select中的goroutine仍然不在频道上监听。