-1

最近我正在为我的 Windows 应用程序寻找一个解决方案,这导致了不正确的文件日志记录。我发现在 windows datetime.now 的解析时间中发现了问题所以,我试图避免这种情况。我根据表格答案中给出的可能提示创建了示例应用程序......

这是示例代码及其输出....

private Timer tmr = new Timer();
private Stopwatch ss;

void tmr_Tick( object sender, EventArgs e )
{
     WriteData();
}

private void button1_Click( object sender, EventArgs e )
{
     ss = new Stopwatch();
     ss.Start();
     tmr.Interval = 1;
     tmr.Tick += new EventHandler( tmr_Tick );
     tmr.Start();
     WriteData();
}
void WriteData()
{
     Console.WriteLine(ss.ElapsedMilliseconds);
}

输出:0 19 29 49 69 79 99 109 122 142 162 172 192 202 222 232 252 282 272 294 314 334 341

我怎样才能在这段代码中获得更高的准确性....????

4

2 回答 2

3
 tmr.Interval = 1;

这是一个很小的数字,不能让它变小是一种美德。但是不,你不会得到一个每秒滴答一千次的计时器。决定 Timer 触发 Tick 事件的实际速率的三个因素:

  • 你的 UI 线程做其他事情有多忙。就像运行 Click 事件处理程序或绘制窗口一样。Tick 事件处理程序只能在 UI 线程不忙时运行,它必须处于空闲状态. 这当然是一个高度可变的数字,对于您的应用程序的用户正在做什么非常重要。他可以做的一些事情非常昂贵,比如调整应用程序的窗口大小。典型的 UI 线程任务,如绘画,需要几毫秒。对人眼来说足够快,但如果您需要该 Tick 事件如此频繁地运行,则非常明显。它只是不会运行,如果你的 UI 很复杂,可能需要数百毫秒。您将需要另一种 Timer 类来避免这种情况,System.Threading.Timer 或 System.Timers.Timer。它们是在线程池线程上运行代码的计时器类。其优点是它不会被 UI 线程中发生的事情延迟。还有一个显着的缺点是你必须非常小心你在该代码中所做的事情,你当然可以'

  • 机器在做其他事情时有多忙。就像执行另一个进程或设备驱动程序的线程一样。这是操作系统的主要任务,允许数百个线程共享处理器。Windows 中的线程调度程序确定线程何时有机会运行。一旦它获得处理器并执行,它就被允许运行一段时间,这段时间称为量子。对于前台窗口所拥有的线程,典型的时间间隔是 45 毫秒。其他线程被挂起,直到它们有机会运行。这当然意味着你计划每秒运行一千次 Tick 事件的计划。您需要仔细控制机器上运行的其他程序,多核处理器将有很大帮助。

  • Windows 多久更新一次计时器。与之相关的核心操作系统功能是时钟中断。用于很多事情,它是操作系统的基本心跳。处理器的正常状态是什么都不做,它是关闭的。时钟滴答中断将其从暂停状态唤醒,操作系统检查是否需要执行任何工作。默认中断率为每秒 64 次,每 15.625 毫秒一次。这也会影响计时器的准确性,当处理器停止时不会发生任何事情。所以按照设计,你永远不会得到 1 毫秒的速率,它永远不会低于 16 毫秒。

后一个子弹肯定是最大的障碍。实际上,您可以在程序中更改中断率,它需要对 timeBeginPeriod() winapi 函数进行 pinvoke 调用。 这个答案显示了如何。还提到了 timeSetEvent() 函数,您需要在任何接近计时器的地方获得该函数,该计时器具有能够维持 1 毫秒速率的合理保证。

于 2013-05-24T08:43:56.067 回答
2

秒表在后台使用 Windows 性能计数器 API,因此它应该非常准确(性能计数器 API 被引用为具有低微秒范围内的分辨率)。

但是,您的测试无效,因为您实际上是在测量计时器中的漂移,而不是秒表中的任何不准确性。

关于 Timer - 看起来您在示例中使用 System.Windows.Forms.Timer 类。这不是准确性的好选择。它的唯一优点是它会在 UI 线程上引发 Tick 事件。请改用 System.Threading.Timer 类 - 您可能会发现它的结果更接近您的预期(尽管仍然存在一些偏差)。

var timerCallback = (TimerCallback)(sw => Console.WriteLine(((Stopwatch)sw).Elapsed));
var timerInterval = TimeSpan.FromMilliseconds(1);
var stopwatch = Stopwatch.StartNew();
var timer = new Timer(timerCallback, stopwatch, timerInterval, timerInterval);

归根结底,要测试秒表的准确性是相当困难的,因为没有任何其他现成的东西更准确!

于 2013-05-24T06:51:33.660 回答