如何停止时间。当前正在另一个go-routine中监听的计时器?

时间:2018-06-28 09:55:02

标签: go timer

我在goroutine中select打开了一个 idle timeout 计时器,如果我看到要取消该计时器的活动。

我看过the documentation,但我不太肯定我对它说的是什么。

  

func (t *Timer) Stop() bool
  停止可防止定时器触发。如果调用停止计时器,则返回true;如果计时器已过期或已停止,则返回false。停止并不会关闭通道,以防止从通道读取失败。

     

为防止使用NewTimer创建的计时器在调用Stop之后触发,请检查返回值并清空通道。例如,假设尚未从t.C收到该程序:

     

if !t.Stop() { <-t.C }

     

不能与计时器通道的其他接收同时进行。

我试图了解何时必须手动排干通道。

我会列出我的理解,如果我错了,请纠正我。

如果Stop返回了false,则意味着:

  • 计时器已停止
    • 在这种情况下,从通道读取将被阻止,所以我不应该这样做
  • 计时器已过期
    • 由于我在频道上正在监听另一个goroutine,因此我可以确定是否已接收到该事件吗?
    • 在文档的“不能与其他收货同时完成” 部分中,这似乎不是一个选择,所以我该怎么办?

对于我来说,从计时器中获取多余的事件没什么大不了的,这是否表明我应该在这里做什么?

1 个答案:

答案 0 :(得分:4)

您可能需要耗尽通道的原因是因为goroutine的调度方式。

问题

想象一下这种情况:

  1. 创建了一个计时器
  2. 计时器触发,向t.C发送一个值
  3. 还没有收到/读取值,但是调用了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
}

类似上面的东西。可能发生的事情之一:

  1. 可以从doWork完成工作,一切顺利。
  2. 计时器可能会触发,导致超时,从而中断选择。
  3. 呼叫停止,停止计时器,只有doWork()才能完成选择。
  4. 计时器可能触发,另一个例程调用t.Stop(),但它也是如此 延迟,因为已发送值,导致超时,中断 选择。

只要您对情况4满意,就无需在调用Stop.后互动/耗尽频道

如果您对情况4不满意,则仍然无法排空t.C频道,因为还有另一个goroutine在监听它。这可能会阻塞if语句。相反,您必须找到另一种布置代码的方法,或者确保select中的goroutine仍然不在频道上监听。