为了账号安全,请及时绑定邮箱和手机立即绑定

使用timer.Reset()的定时器示例不按描述工作

使用timer.Reset()的定时器示例不按描述工作

Go
慕少森 2023-07-31 16:24:42
我一直在尝试让我的第一个“go 例程”运行的示例,当我让它运行时,它不会按照 go 文档中规定的带有 timer.Reset() 函数的方式工作。就我而言,我相信我这样做的方式很好,因为我实际上并不关心 chan 缓冲区中有什么(如果有的话)。这样做的目的是,case <-tmr.C:如果发生任何事情,就会触发case _, ok := <-watcher.Events:,然后一切都会安静至少一秒钟。这样做的原因是,case _, ok := <-watcher.Events:事件的间隔可能是一到几十微秒,我只关心它们全部完成并且事情再次稳定下来。然而我担心按照文档所说的“必须做”的方式做是行不通的。如果我知道做得更好,我会说文档是有缺陷的,因为它假设缓冲区中有东西,而实际上可能没有,但我不知道做得足够好,有信心做出这样的决定,所以我希望一些专家出来那里可以启发我。下面是代码。我还没有把它放在操场上,因为我必须做一些清理(删除对程序其他部分的调用),并且我不确定如何让它对文件系统更改做出反应以显示它的工作。我已经在代码中清楚地标记了哪些替代方案有效,哪些无效。
查看完整描述

2 回答

?
HUWWW

TA贡献1874条经验 获得超12个赞

例如,假设程序尚未从 tC 接收到:


if !t.Stop() {

        <-t.C

}

这不能与来自计时器通道的其他接收同时完成。

有人可能会说这不是一个很好的例子,因为它假设计时器在您调用时正在运行t.Stop但它确实继续提到,如果已经有一些现有的 Goroutine 正在或可能正在读取t.C.

Reset文档重复了所有这些,并且顺序有点错误,因为Reset排序在 之前Stop。)

本质上,整个地区都有点令人担忧。没有好的通用答案,因为在返回t.Stop呼叫期间至少存在三种可能的情况:

  • 没有人正在收听该频道,并且频道中现在没有定时器滴答声。如果计时器在调用之前t.Stop已停止,通常会出现这种情况。如果计时器已经停止,t.Stop则始终返回 false。

  • 没有人正在收听该频道,并且频道中现在有计时器滴答声。当计时器正在运行但t.Stop无法阻止其触发时,总是会出现这种情况。在这种情况下,t.Stop返回 false。当计时器正在运行但在您调用之前t.Stop就触发了,因此它自己停止了,所以t.Stop无法停止它并返回 false。

  • 其他人正在收听该频道。

在最后一种情况下,您不应该执行任何操作。在第一种情况下,您不应该执行任何操作。在第二种情况下,您可能希望从通道接收以便将其清除。这就是他们的例子的目的。

有人可能会争辩说:

if !t.Stop() {

        select {

        case <-t.C:

        default:

        }

}

是一个更好的例子。它执行一次非阻塞尝试,该尝试将消耗计时器滴答(如果存在),如果没有计时器滴答,则不执行任何操作。无论您调用 时计时器是否实际运行,这都有效t.Stop。事实上,如果t.Stopreturns ,它甚至可以工作true,尽管在这种情况下,t.Stop停止了计时器,因此计时器从未设法将计时器滴答放入通道中。(因此,如果通道中存在数据,则它必然是先前清除通道失败所遗留的。如果不存在此类错误,则尝试接收也就不必要了。)


但是,如果其他人(其他一些 goroutine)正在或可能正在读取该通道,那么您根本不应该执行此操作。尽管调用了 ,但无法知道谁(您或他们)将获得通道中可能存在的任何计时器滴答声Stop。


同时,如果您不打算进一步使用计时器,那么在通道中留下一个计时器滴答声(如果有的话)相对无害。当通道本身被垃圾收集时,它也会被垃圾收集。当然,这是否明智取决于您对计时器所做的事情,但在这些情况下,只需调用t.Stop并忽略其返回值就足够了。


查看完整回答
反对 回复 2023-07-31
?
饮歌长啸

TA贡献1951条经验 获得超3个赞

您创建一个计时器并立即停止它:

var tmr = time.NewTimer(time.Second)
tmr.Stop()

这没有任何意义,我认为这只是你的“意外”。

但更进一步,在循环内部:

    case _, ok := <-watcher.Events:

当发生这种情况时,您声称这不起作用:

        if !tmr.Stop() {
            fmt.Println(`Ticker: CHAN DRAIN`)
            <-tmr.C // STOPS HERE AND GOES NO FURTHER
        }

Timer.Stop()记录true如果此调用停止计时器以及false计时器已停止(或到期)则返回。但是你的计时器在创建后就已经停止了,所以正确tmr.Stop()返回false,所以你进入if并尝试从 接收tmr.C,但由于计时器“长时间”停止,因此不会在其通道上发送任何内容,因此这是一个阻塞(永远)操作。

如果您是使用 显式停止计时器的人timer.Stop(),则推荐的用于耗尽其通道的“模式”没有任何意义,并且不适用于第二次调用Timer.Stop()


查看完整回答
反对 回复 2023-07-31
  • 2 回答
  • 0 关注
  • 307 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信