admin 管理员组

文章数量: 887021

背景

  在实际工作过程中,比如与硬件交互、媒体视频播放、性能分析以及多线程任务同步时,可能需要在windows平台下实现ms级别精度的定时器。然而,这种需求虽然存在,但是由于windows系统并不是一个实时操作系统,实现这种精度的定时器,并不是一件容易的事情。

Windows 不是实时操作系统,所以任何方案都无法绝对保证定时器的精度,只是能尽量减少误差。所以,系统的稳定性不能完全依赖于定时器,必须考虑失去同步时的处理。

等待策略

  要想实现高精度定时器,必须需要等待和计时两种基础功能,等待用来跳过一定时间间隔,计时可以进行时间检查,用以调整等待事件。
等待策略实际就是两种:
1. 自旋等待:让CPU空转消耗时间,占用大量CPU时间,但是时间高度可控。
2. 阻塞等待:线程进入阻塞状态,让出CPU时间片,在等待一定时间后再由操作系统调度回到运行状态。阻塞时不占用CPU,然而需要操作系统调度,时间难以控制。
  可以看到二者各有优劣,应该按照不同需求进行不同的实现。
所以难点在于等待策略,下面先分析简单的自旋等待。

自旋等待

伪代码如下:


等待开始时间=当前计时
while((当前计时-等待开始时间)<需要等待的时间)    
{
    自旋;
}
阻塞等待

  阻塞等待会把控制权交给操作系统,这样就必须确保操作系统能够及时的将定时器线程调度回运行状态。默认情况下,Windows的系统定时器精度为15.625ms,也就是说时间切片是这个尺寸。如果线程阻塞,出让其时间片进行等待,再被调度运行的时间至少是一个切片15.625ms。那么必须减少时间切片的长度,才能实现更高的精度。
 一般实现阻塞等待都用的是thread库的sleep函数,实测sleep函数的精度不高,估计也就ms级的,也就是可能会产生+1ms的误差。
 sleep会出让剩余的cpu时间片给优先级相同的线程,而yield则是出让剩余的cpu时间片给运行在同一核心上的线程。在出让的时间片结束后,其会被重新调度。一般情况下,整个过程可以在1ms内完成。

另外,sleep或者yield在CPU高负载情况下非常不稳定,实测可能会阻塞高达几ms的时间,所以可能会产生更多的误差

本文标签: 定时器 系统 Windows