3 回答
TA贡献1770条经验 获得超3个赞
time.After我使用函数简化了代码:
package main
import (
"log"
"math/rand"
"time"
)
const TimeOutTime = 3
const MeanArrivalTime = 4
func main() {
const interval = time.Second * TimeOutTime
// channel for incoming messages
var incomeCh = make(chan struct{})
go func() {
for {
// On each iteration new timer is created
select {
case <-time.After(interval):
time.Sleep(time.Second)
log.Println("Do task")
case <-incomeCh:
log.Println("Handle income message and move to the next iteration")
}
}
}()
go func() {
for {
time.Sleep(time.Duration(rand.Intn(MeanArrivalTime)) * time.Second)
// generate incoming message
incomeCh <- struct{}{}
}
}()
// prevent main to stop for a while
<-time.After(10 * time.Second)
}
注意:
After等待持续时间过去,然后在返回的通道上发送当前时间。它相当于NewTimer(d).C。在定时器触发之前,垃圾收集器不会恢复底层定时器。如果效率是一个问题,请改用并在不再需要计时器时NewTimer 调用。Timer.Stop
TA贡献1797条经验 获得超4个赞
您可能会考虑不同的整体设计。
例如,假设我们编写了一个名为 Deadliner 的例程或接口——如果你愿意,它可以成为自己的包,或者只是一个接口,我们将看到与 Go 已有的东西非常相似——描述了它的工作/合同这边走:
Deadliner 的用户可以随时创建截止日期。
Deadliner 会一直等到最后期限出现,然后将最后期限标记为已出现。
任何 Go 例程都可以随时取消 Deadliner。这会将截止日期标记为已取消,因此等待它的任何人都将停止等待,并且可以判断他们停止等待的原因是“取消”(而不是“过期”)。它也有助于清理 gc 的资源,以防你创建了很多 Deadliner,然后在它们的超时触发之前丢弃它们。
现在在您的最高级别,在您开始等待消息之前,您只需设置一个截止日期。这不是一个计时器(即使它可能在内部使用一个),它只是一个 Deadliner 实例。然后你等待两个事件之一:
d, cancel = newDeadline(when)
for {
select {
case <-d.Done():
// Deadline expired.
// ... handle it ...
d, cancel = newDeadline(when) // if/as appropriate
case m := <-msgC:
// got message - cancel existing deadline and get new one
cancel()
d, cancel = newDeadline(when)
// ... handle the message
}
}
现在我们只注意到 Go 已经有了这个:它在 package 中context。 d是一个上下文;newDeadline是context.WithDeadline或context.WithTimeout(取决于您是要自己计算截止时间,还是让超时代码为“现在”添加持续时间)。
无需摆弄计时器和时间滴答通道,也无需分拆您自己的单独 goroutine。
如果最后期限没有在单个消息上重置,而是在特定消息组合上重置,您只需将其写在您的case <-msgChan部分中。如果当前没有通过通道接收消息,请通过将消息放入通道来实现,这样您就可以使用这个非常简单的等待截止日期或消息模式。
TA贡献1793条经验 获得超6个赞
假设你有:
t.Stop()
t.Reset()
如果计时器在调用之前停止并耗尽Stop,则可以正常工作。Stop如果同时停止计时器和计时器滴答声,问题就会显现出来。然后你可能会遇到一个停止的计时器,一个 goroutine 等待写入t.C通道。Stop如果仍有一个 goroutine 等待写入,则返回 false ,t.C并且您必须从中读取。否则,你将让那个 goroutine 无限期地在那里等待。
因此,正如您已经观察到的,您必须这样做:
if !t.Stop() {
<-t.C
}
t.Reset(d)
但是,即便如此,我认为您的解决方案存在缺陷,因为使用了异步重置。相反,请尝试为每个模拟事件使用一个新计时器。
- 3 回答
- 0 关注
- 89 浏览
添加回答
举报