- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个场景,我正在处理 channel 上的事件,其中一个事件是需要在特定时间范围内发生的心跳。不是心跳的事件将继续消耗计时器,但是无论何时收到心跳,我都想重置计时器。显而易见的方法是使用 time.NewTimer
.
例如:
func main() {
to := time.NewTimer(3200 * time.Millisecond)
for {
select {
case event, ok := <-c:
if !ok {
return
} else if event.Msg == "heartbeat" {
to.Reset(3200 * time.Millisecond)
}
case remediate := <-to.C:
fmt.Println("do some stuff ...")
return
}
}
}
请注意 time.Ticker
不会在这里工作,因为只有在未收到心跳时才应触发补救措施,而不是每次都收到。
上述解决方案适用于我尝试过的少数低容量测试,但是我遇到了一个 Github 问题,表明 resetting a Timer which has not fired is a no-no .此外,文档指出:
Reset should be invoked only on stopped or expired timers with drained channels. If a program has already received a value from t.C, the timer is known to have expired and the channel drained, so t.Reset can be used directly. If a program has not yet received a value from t.C, however, the timer must be stopped and—if Stop reports that the timer expired before being stopped—the channel explicitly drained:
if !t.Stop() {
<-t.C
}
t.Reset(d)
这让我停顿了一下,因为它似乎准确地描述了我正在尝试做的事情。我正在重置 Timer
每当收到心跳时,在它被触发之前。我对 Go 的经验还不够丰富,无法理解整篇文章,但看起来我可能正走在一条危险的道路上。
我想到的另一个解决方案是简单地替换 Timer
每当心跳发生时使用一个新的,例如:
else if event.Msg == "heartbeat" {
to = time.NewTimer(3200 * time.Millisecond)
}
起初我担心重新绑定(bind)to = time.NewTimer(3200 * time.Millisecond)
在选择中不可见:
For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send.
但在这种特殊情况下,由于我们处于循环中,我希望在每次迭代时我们重新输入选择,因此新绑定(bind)应该是可见的。这是一个公平的假设吗?
我意识到那里有类似的问题,我已经尝试阅读相关的帖子/文档,但我是 Go 的新手,只是想确保我在这里理解正确。
所以我的问题是:
我用的是timer.Reset()
不安全,还是 Github 问题中提到的案例突出了其他不适用于此处的问题?文档中的解释含糊不清,还是我只需要更多 Go 经验?
如果它不安全,我提出的第二个解决方案是否可以接受(在每次迭代时重新绑定(bind)计时器)。
附录
!t.Stop()
进行测试因为 Stop 的错误返回表明计时器已经触发,因此必须在调用 Reset 之前耗尽。
我仍然不明白的是,为什么需要调用 t.Stop()
在 t.Reset()
之前, 当Timer
尚未开火。据我所知,没有一个例子涉及到这一点。
最佳答案
What I still do not understand, is why it is necessary to call t.Stop() prior to t.Reset(), when the Timer has yet to fire.
“当计时器尚未触发时”位在这里很关键。计时器在一个单独的 go 例程(runtime 的一部分)中触发,这可能随时发生。您无法知道在您调用 to.Reset(3200 * time.Millisecond)
时计时器是否已触发。 (它甚至可能在该函数运行时触发!)。
这是一个演示这一点的示例,它与您正在尝试的有点相似(基于 this ):
func main() {
eventC := make(chan struct{}, 1)
go keepaliveLoop(eventC )
// Reset the timer 1000 (approx) times; once every millisecond (approx)
// This should prevent the timer from firing (because that only happens after 2 ms)
for i := 0; i < 1000; i++ {
time.Sleep(time.Millisecond)
// Don't block if there is already a reset request
select {
case eventC <- struct{}{}:
default:
}
}
}
func keepaliveLoop(eventC chan struct{}) {
to := time.NewTimer(2 * time.Millisecond)
for {
select {
case <-eventC:
//if event.Msg == "heartbeat"...
time.Sleep(3 * time.Millisecond) // Simulate reset work (delay could be partly dur to whatever is triggering the
to.Reset(2 * time.Millisecond)
case <-to.C:
panic("this should never happen")
}
}
}
在 playground 中尝试.
由于 time.Sleep(3 * time.Millisecond)
,这可能显得做作但这只是为了始终如一地证明这个问题。您的代码可能在 99.9% 的时间内都有效,但事件和计时器 channel 总是有可能在 select
之前触发。正在运行(其中 random case 将运行)或当 case event, ok := <-c:
中的代码运行时 block 正在运行(包括 Reset()
正在进行中)。发生这种情况的结果是意外调用 remediate
。代码(这可能不是什么大问题)。
幸运的是解决这个问题相对容易(遵循 documentation 中的建议):
time.Sleep(3 * time.Millisecond) // Simulate reset work (delay could be partly dur to whatever is triggering the
if !to.Stop() {
<-to.C
}
to.Reset(2 * time.Millisecond)
在 the playground 中试试这个.
这是有效的,因为 to.Stop
returns “如果调用停止计时器则为 true,如果计时器已经过期或已停止则为 false”。请注意,如果 timer is used in multiple go-routines,事情会变得更加复杂。 “这不能与来自定时器 channel 的其他接收或对定时器停止方法的其他调用同时完成”,但在您的用例中情况并非如此。
Is my use of timer.Reset() unsafe, or are the cases mentioned in the Github issue highlighting other problems which are not applicable here?
是的——这是不安全的。然而,影响相当低。事件到达和计时器触发需要几乎同时发生,在这种情况下,运行 remediate
代码可能不是一个大问题。请注意,修复非常简单(根据文档)
If it is unsafe, is my second proposed solution acceptable (rebinding the timer on each iteration).
您提出的第二个解决方案也有效(但请注意,垃圾收集器在触发或停止之前无法释放计时器,如果您快速创建计时器,这可能会导致问题)。
注意:回复@JotaSantos 的建议
Another thing that could be done is to add a select when draining <-to.C (on the Stop "if") with a default clause. That would prevent the pause.
参见 this comment有关为什么这可能不是一个好方法的详细信息(在您的情况下也没有必要)。
关于在选择循环中重置 timer.NewTimer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66037676/
我正在使用计时器以相当长的间隔(2分钟)定期运行事件。一切正常。但是,我希望在创建计时器时立即触发该事件(而不是等待2分钟)。 请注意,我不能仅通过调用方法来执行此操作,因为它需要一些时间才能运行,并
我正在检查一些可怕的遗留代码,这些代码具有 Timer 事件以及一些包含 DoEvents 调用的冗长代码。简化版如下所示: Private Sub tmrProcess_Timer()
我正在尝试创建一个 Windows 窗体应用程序,我想实现一个计时器。 public void timerStart() { DateTime now = DateTime
我正在创建一个新类,以便使 System.Timers.Timer 类更好地满足我的需要。我像这样创建我的新类...... using System.Timers; class BtElapsedEv
我最近一直在检查一些可能的计时器,并且 System.Threading.Timer和 System.Timers.Timer对我来说是必要的(因为它们支持线程池)。 我正在制作一款游戏,我计划使用所
首先我要说的是,与其说这是一个需要解决的问题,不如说这是一个问题。我现在有了解决方案,对我来说一切正常。但是我想知道为什么第一次出现问题。 这是我现在拥有的代码,它的工作方式与我预期的一样:
在本文中:http://msdn.microsoft.com/en-us/magazine/cc164015.aspx作者声明 System.Threading.Timer 不是线程安全的。 从那时起
背景: 我有一个计时器,我用它来跟踪自 serialPort DataReceived 事件被触发以来已经过了多长时间。我正在为此创建自己的解决方案而不是使用内置的超时事件,因为我正在获取连续的数据流
我正在查看 Flutter Timer代码和 cancel() 方法。当我想删除一个计时器时,我可以使用这两行: timer.cancel(); timer = null; 或者我可以这样做: 定时器
我正在尝试使用 C# 中的计时器以五秒的间隔运行一个方法。虽然这段代码似乎不起作用。运行它时我没有收到任何错误,但程序(我在控制台中运行)在 IP.timer1.Start() 之后立即关闭。 tim
我正在尝试使用 C# 中的计时器以五秒的间隔运行一个方法。虽然这段代码似乎不起作用。运行它时我没有收到任何错误,但程序(我在控制台中运行)在 IP.timer1.Start() 之后立即关闭。 tim
我有错误显示: 'Timer' is an ambiguous reference between 'System.Windows.Forms.Timer' and 'System.Threading
在我的应用程序中,我必须定期向“兄弟”应用程序发送心跳。 使用 System.Timers.Timer/Threading.Timer 或使用带有 while 循环和 Thread.Sleep 的线程
我最近遇到了编写 Windows 服务的挑战。我需要定期请求一个 URL 并检查它的可用性。为此,我决定在 OnStart 中初始化一个计时器。服务方法并在 timer_Tick 中完成所有工作事件。
看来 System.Timers.Timer 实例通过某种机制保持事件状态,但 System.Threading.Timer 实例则不然。 示例程序,具有定期System.Threading.Time
我做了一个 goog.Timer对象 ( http://closure-library.googlecode.com/svn/docs/class_goog_Timer.html ) 与 new go
当您处理“原始”.net 计时器时,您可以传入等待句柄以在 Win32 计时器被销毁后调用,并且您可以假设您的回调不会被调用。 (并且计时器将被 GC 视为“死”) 如何使用 System.Timer
我想让 deoplete 自动完成建议弹出得更快,这需要设置 g:deoplete#auto_complete_delay help 说这需要 +timers 支持。如何在配置中启用此计时器? 谢谢!
我想知道是否有合理的方法来确定 Timers.Timer 对象的负载能力?或者,更确切地说,线程功能。有人知道使用 Timer 类启动大量(数百个)线程是否有任何问题? 编辑:为了工作,我们将启动一些
我正在创建一个 WindowsService,它的 Timer 绑定(bind)到具有无限循环的 EventHandler;我希望能够在服务的 OnStart() 方法中触发此计时器一次,然后在服务的
我是一名优秀的程序员,十分优秀!