.NET秒表类可以这么糟糕吗?

时间:2010-08-03 19:49:56

标签: .net stopwatch

我正在制作一个需要非常紧凑的时间的应用程序,而秒表类是完美的解决方案。但是,有时候我注意到,当在小型平板电脑上运行时,秒表值已经过时了。我添加了一些调试打印输出,每隔200毫秒左右监视一次秒表的值:

  

0:00:197
  0:00:502个
  0:00:702个
  ...
  的 0:03:356
  0:12:93

  0点13分21秒
  0:13:421个
  ...

怎么可能从~3秒跳到~13秒?我现在看到底层函数QueryPerformanceCounter()是错误的( Beware of QueryPerformanceCounter() ),但我感觉到其他东西在这里发生了。

感谢任何见解。

更新

这里有关于我的代码的更多细节:它非常简单。它是一个WPF应用程序,它在启动时创建一个新的Stopwatch对象,然后通过Start()将其踢出。然后我创建一个DispatcherTimer,如下所示:

displayTimer = new DispatcherTimer(); 
displayTimer.Tick += display_Tick; 
displayTimer.Interval = DISPLAY_INTERVAL_TIMESPAN; 

其中时间跨度为200毫秒。每次Stopwatch滴答时,我的调试代码都会打印出dispatchTimer对象的值。

UPDATE2:

有趣的Microsoft支持文章是 Performance counter value may unexpectedly leap forward

3 个答案:

答案 0 :(得分:17)

更新(看到您的日志后)

正如您已经提到的,Stopwatch类使用下面的QueryPerformanceCounter函数。在备注部分,MSDN说:

  

在多处理器计算机 上,调用哪个处理器无关紧要。但是, 由于基本输入/输出系统(BIOS)或硬件抽象层(HAL)中的错误,您可以在不同的处理器上获得不同的结果 。要指定线程的处理器关联,请使用SetThreadAffinityMask函数。

当您使用Dispatcher时,每次查询经过的时间时,QueryPerformanceCounter可能不会在同一个CPU上执行。

您可以通过为流程指定处理器亲缘关系来检查MSDN中提到的问题是否是您遇到问题的原因,例如:通过使用start命令调用可执行文件。 10秒似乎是CPU与我之间的一个很大的滞后,但文档对于差异有多大是非常模糊的。以下命令将应用程序绑定到第一个CPU:

> start.exe /AFFINITY 1 program.exe

如果这样可以解决问题,您可能需要查看建议的解决方法,即在查询SetThreadAffinityMask对象之前调用Stopwatch函数。

您的评论说您使用的是WPF DispatcherTimer。该类的documentation表示:

  

不保证计时器在时间间隔发生时完全执行 ,但保证在时间间隔发生之前不执行计时器。这是因为DispatcherTimer操作与其他操作一样放在Dispatcher队列中。 DispatcherTimer操作执行时依赖于队列中的其他作业及其优先级。

这意味着计时器事件可能会延迟,特别是如果调度程序忙于其他任务。您是否将其他内容放在调度程序队列中以防止事件先前触发?

答案 1 :(得分:15)

PC硬件的准确时间并不简单,期间。以下是一些资源,展示了一些Windows时序的经验,这将是Windows环境中任何计数器的底层实现,包括.NET:

它解释了为什么有时你会得到10毫秒的分辨率,这取决于你使用的系统调用,并且更多地说明了为什么QueryPerformanceCounter是“buggy”。其中一个要点是省电模式/可变CPU速度会干扰这些时间。

与此相关的概念是在实时物理模拟中“锁定时间步长”。如果你谷歌那么,你可能会得到一些关于如何解决你遇到的问题的想法。基本概念是您有固定的时间步骤,并为您的计时/更新功能执行一种生产者/消费者实现。我不知道这是否适用于您的特定域名,但它被视为视频游戏中的最佳做法。

答案 2 :(得分:2)

也许我误解了你的问题,但是你需要只测量时间间隔,还是需要在这些时间间隔执行代码?如果你需要执行代码,那么检查System.Timers.Timer类,因为它是线程安全的,并且应该适用于多处理器。