go - 如何停止当前正在另一个 go-routine 中收听的 time.Timer?
问题描述
我在 goroutine 中有一个空闲超时计时器,如果我看到我想取消计时器的活动。select
我查看了文档,但我不确定我很清楚它说的是什么。
func (t *Timer) Stop() bool
停止可防止定时器触发。如果调用停止计时器,则返回 true,如果计时器已过期或停止,则返回 false。Stop 不会关闭通道,以防止从通道读取错误成功。要防止使用 NewTimer 创建的计时器在调用 Stop 后触发,请检查返回值并耗尽通道。例如,假设程序还没有从 tC 收到:
if !t.Stop() { <-t.C }
这不能与来自定时器通道的其他接收同时完成。
我试图了解何时必须手动排空频道。
我会列出我的理解,如果我错了,请纠正我。
如果Stop
返回false
,这意味着:
- 计时器已经停止
- 在这种情况下,从频道读取会阻塞,所以我不应该这样做
- 计时器已过期
- 由于我有另一个 goroutine 在频道上监听,我可以确定它是否收到了事件吗?
- 从文档的“这不能与其他接收同时完成”部分看来,这不是一个选项,那么我该怎么办?
在我的情况下,从计时器获得一个多余的事件没什么大不了的,这是否告诉我应该在这里做什么?
解决方案
您可能需要耗尽通道的原因是 goroutine 的调度方式。
问题
想象一下这种情况:
- 创建了一个计时器
- 定时器触发,发送一个值到
t.C
- 尚未收到/读取该值,但
t.Stop()
已被调用。
在这种情况下,通道上有一个值t.C
,并t.Stop()
返回 false,因为“计时器已过期”(即,当它在 上发送值时t.C
)。
文档说“这不能与其他接收同时完成”的原因是因为不能保证 theif !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 没有仍在监听通道。
推荐阅读
- sql - 如何拆分作为具有可变长度的部分字符串部分编号的 SQL 列
- python - 如何使用熊猫数据框计算点之间的距离?
- javascript - 让 Discord 机器人命令不区分大小写?
- elasticsearch - 如何在 Elasticsearch 中存储商店产品
- sql - 如何在组合查询中编辑字段
- google-app-maker - 如何在谷歌应用程序制造商中使用@expr 数据进行绑定转换器?
- vb.net - 如何将对象传递给默认表单实例
- java - 来自 ExpandableListView 的子元素中的 setOnClickListener
- r - 根据用户输入日期添加新的计算列
- jenkins - Jenkins 多分支与不同仓库中的 Jenkinsfile