比如间隔 10ms,任务 7-12ms,则会按照等待 10ms 、任务 7ms、等待 3ms、任务 12ms(超时 2ms 失去同步)、任务 7ms、等待 1ms(回到同步)、任务 7ms、等待 3ms、… 进行。就是尽量按照设定好的时间框架来执行任务,只要任务不是始终超时,就可以回到原本的时间框架上。
var 下一帧时间 = 0;
while(定时器开启)
{
下一帧时间 += 间隔时间;
while (当前计时 < 下一帧时间)
{
等待;
}
触发任务;
}
(2)可推迟时间框架:
上面的例子会按照等待 10ms 、任务 7ms、等待 3ms、任务 12ms(超时,推迟时间框架 2ms)、任务 7ms、等待 3ms、… 进行。超时的任务会推迟时间框架。
var 下一帧时间 = 0;
while(定时器开启)
{
下一帧时间 += 间隔时间;
if (下一帧时间 < 当前计时)
下一帧时间 = 当前计时
while (当前计时 < 下一帧时间)
{
等待;
}
触发任务;
}
(3)固定等待时间
上面的例子会按照等待 10ms、任务 7ms、等待 10ms、任务 12ms、等待 10ms、任务 7ms… 进行。等待时间始终不变。
while(定时器开启)
{
var 等待开始时间 = 当前计时;
while ((当前计时 - 等待开始时间) < 间隔时间)
{
等待;
}
触发任务;
}
// 或者:
var 下一帧时间 = 0;
while(定时器开启)
{
下一帧时间 += 间隔时间;
while (当前计时 < 下一帧时间)
{
等待;
}
触发任务;
下一帧时间 = 当前计时;
}
如果使用多媒体定时器(timeSetEvent),它固定实现了第一种模式,而其它的等待策略能够实现全部三种模式,可以根据需求选择。
在while循环中的等待可以使用自旋或阻塞,也可以结合它们来达到精度、稳定性和 CPU 开销的平衡。
另外,由上面的伪代码可以看出,这三种模式的实现可以统一,能够做到根据情况切换。
3.2线程优先级
最好把线程优先级调高,以保证定时器能够稳定工作,减少被抢占的机会。然而需要注意,这在 CPU 资源不足时可能导致低优先级线程的饥饿。也就是说不能让高优先级线程去等待低优先级线程改变状态,很有可能低优先级线程没有机会运行,导致死锁或类似死锁的状态。(见一种类似的饥饿的例子)
线程的最终优先级和进程的优先级有关,所以有时候也需要提高进程优先级(见 C# 中的多线程系列的线程优先级说明)。










