如果您在完成之前再次调用打勾方法,则问题不在于您的计时器。这是因为处理时间超过了 16.6 毫秒。获得更好的计时器并不能解决您的问题。
也就是说,您可以通过几种方式防止重入。
您可以在输入回调时禁用计时器,并在完成后重新启用它。这将防止多次调用:
timer_ticked()
{
timer.Enabled = false;
// do stuff here
timer.Enabled = true;
}
请注意,这并不能为您提供完美的 16.6 毫秒滴答频率。相反,下一个滴答声将在您启用计时器后 16.6 毫秒(大约)发生。您的实际周期是 16.6 毫秒加上处理所需的时间。
唯一会失败的情况是timer_ticked
在下一次滴答发生之前没有调用该方法。
如果您想保证无法获得并发滴答,可以使用 aSystem.Threading.Timer
并将其设置为一次性(无周期性信号)。例如:
Timer myTimer = new Timer(timer_tick, null, 16, -1);
最后-1
一个参数中的 告诉它不是周期性计时器。它会触发一次并停止。
然后,在你的计时器滴答声中:
timer_tick()
{
// do stuff
// restart the timer
myTimer.Change(16, -1);
}
编辑
如果处理程序仍在处理前一个滴答声,您不能轻易告诉计时器不要发出滴答声。但是,您可以阻止计时器滴答处理程序在后续滴答中执行任何操作。您只需使用Monitor
:
private object timerLock = new object();
timer_ticked()
{
if (!Monitor.TryEnter(timerLock))
return;
try
{
// do stuff here
}
finally
{
Monitor.Exit(timerLock);
}
}
这种解决方案的问题在于,如果您的计时器设置为 16 毫秒并且处理程序需要 17 毫秒,那么您的有效更新率将是每 32 毫秒一次,因为第二个滴答基本上被忽略了。你最好使用一次性计时器交易。
另一种可能性是使用 a Stopwatch
to time 你的处理程序需要多长时间,并从下一个延迟期中减去它:
timer_ticked()
{
var sw = Stopwatch.StartNew();
// do stuff
timer.Change(16-sw.ElapsedMilliseconds, -1);
}
但这并不是那么简单。如果处理程序完成它的时间超过 16 毫秒,那么您最终会得到一个负延迟。所以你会想要:
var delayTime = Math.Max(0, 16 - sw.ElapsedMilliseconds);
timer.Change(delayTime, -1);
但是同样,如果您的处理程序经常花费比计时器延迟更长的时间,那么这对您没有帮助。您要么必须降低定时器频率(即延长延迟),要么优化处理代码。