C#计时器在其间隔时间之前触发

12
我们在使用.NET 2.0中的System.Threading.Timer(定时器)从Windows服务中遇到以下问题:
  1. 大约有12个不同的计时器对象。
  2. 每个计时器都有到期时间和间隔时间,这些已正确设置。
  3. 观察发现,在3至4小时后,计时器开始在其间隔时间之前发出信号。例如,如果计时器应在4:59:59发出信号,则会在4:59:52提前7秒发出信号。
请问有人能告诉我造成此行为的原因以及解决方案吗?
谢谢, Swati
4个回答

10

很好的问题... 原因如下:

当涉及计算机时,“时间”是一件棘手的事情...你永远无法依赖于“间隔”是完美的。一些计算机只会每14到15毫秒“滴答”一次定时器,有些更频繁地执行此操作,有些则不那么频繁。

所以:

Thread.Sleep(1);

运行时间可能在1到30毫秒之间。

如果你需要更精确的计时器,你需要记录开始时间 DateTime,然后在计时器中通过DateTime.Now与原始时间进行减法操作,以确定是否“到达运行代码的时间”。

以下是所需操作的示例代码:

DateTime startDate = DateTime.Now;

然后,使用间隔设置为1毫秒启动计时器。然后,在你的方法中:

if (DateTime.Now.Subtract(startDate).TotalSeconds % 3 == 0)
{
    // This code will fire every 3 seconds.
}

上面的代码将每3秒准时执行。您可以让它运行10年,它仍然会每3秒执行一次。


我知道这只是一个例子,但你是否会将计时器间隔增加到100或者500毫秒?我知道你可能会失去一点精度,但相比每秒触发1000次定时器,而你每3秒才需要一次结果,这样做不是更好吗? - Matt Warren
是的,每秒1000个中断有点太多了。 - Bing Bang

8

Timothy的解释很有说服力,但System.Threading.Timer已经按照这种方式工作。至少在Rotor实现中,它使用GetTickCount()返回的值来计算下一个回调的到期时间。真正的CLR这样做的可能性不大,更有可能使用CreateTimerQueueTimer()。然而,这个API函数也可能依赖于时钟中断递增的内部tick。

问题在于内部时钟tick(由GetTickCount()和Environment.TickCount返回)与绝对墙上时间(由DateTime.Now返回)保持同步的程度。不幸的是,这是你的机器上HAL(硬件抽象层)的实现细节。机器制造商可以提供定制的HAL,这个HAL被调整为与机器的BIOS和芯片组配合使用。

如果机器有两个计时源,一个时钟芯片设计用来跟踪墙上时间,即使你拔掉机器电源也会继续运行,另一个频率可用于分频以提供时钟中断。原始的IBM AT PC就是这样工作的。时钟芯片通常是可信的,如果不准确,时钟将严重偏差。芯片组则不能,削减振荡器质量是省钱的简单方法。

通过避免在定时器上长时间运行并在回调中从DateTime.Now重新计算下一个到期时间来解决您的问题。


谢谢你的帮助。重新计算定时器可能会解决我的问题。我打算尝试一下。再次感谢!! - Swati Ghaisas

0
也许重新调度是在需要几秒钟完成的操作之后完成的。或者你不能控制这个?

0

这是无法控制的,因为它取决于系统的速度。如果您有超级计算机,则可能没有足够的差异,但如果您有普通PC,则在长时间执行后计时器间隔会有所不同。

希望这能帮到您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接