System.Form.Timer中Interval属性对时钟精度的影响。
前情提要
可显示毫秒的计时器
最近写了个可以显示毫秒的时钟。其中用到了窗体计时器,也就是Windows.Form.Timer组件的Interval属性。

因为有精度要求,所以想的是每毫秒更新一次时钟的时间。这样我录个屏,再逐帧截取时钟的图片,就能精确的计算出每一帧直接到底过了多少秒。
this.timer = new System.Windows.Forms.Timer();
this.timer.Interval = 1;
this.timer.Tick += new EventHandler(UpdateLabelTime);
至少理想情况是这样的,直到我发现时钟的时间更新并不是线性的,有时候它的时间更新会肉眼可见的“卡一下”。而且截出来的时间也不是线性关系。
我肯定不会先去怀疑是ffmpeg的误差,毕竟ffmpeg都是发展这么多年的老东西了。我也排除了会不会是我电脑卡顿了的影响。那误差会不会来源于我们自己写的计时器?
Form.Timer.Interval=1
真的能做到每毫秒触发吗?
最有可能出问题的就是这个地方。其他地方可能都是微秒级别的误差。但这里不一样,“每毫秒”触发一次事件,这意味着它的误差很有可能是毫秒级别的。
那首先就是百度看看有没有什么说法。结果还真百度出来了点东西。一个是微软官方文档的说法

另一个是stack overflow上的解答

一句话。Time Interval=1 ≠ 真的每毫秒触发一次。为什么呢?
先来看看System.Form.Timer的原理。Timer组件实际上就是一个计时器。假设Interval =1 ,那么Timer组件就会在1毫秒后向系统的消息队列发送计时器的信息。问题来了,消息队列是实时的吗?
- Windows不是实时操作系统(RTOS),任务的调度方式用的RR,分配给进程的时间片用完了就切换到下一个进程。这个切换(原进程被delay)的过程就会导致误差。像其他的实时操作系统就是按任务优先级去调度,一般来说高优先级的任务会一直运行。
还有一点则和系统时钟本身有关。先看看stack overflow:

更具体的话涉及到Windows中“时钟中断”的概念,有兴趣的可以自行了解。简单来说就是你虽然想让定时器以“一毫秒”的频率去触发事件,但是操作系统分辨不出“一毫秒”是什么,它只知道“十毫秒”是什么。所以系统只能以“十毫秒”的频率去触发事件。
不过这里“不精确”只是针对System.Form.Timer这个组件而言的,要高精度的话msdn也说了可以用System.Timers。不过我没测过后面这个,现在测了Sytem.Form.Timer之后发现确实是有毫秒级别的误差。当然了,时分秒这些肯定是准的。而毫秒的误差范围也挺大的,不知道是ffmpeg还是其他后台进程的影响,误差区间大概为1ms-20ms这样。