线程和计时器内的计时器

时间:2013-04-02 14:39:48

标签: c# .net multithreading gdi emulation

我正在用C#开发一个Chip-8仿真器,我几乎已经完成了几乎所有方面,但我仍然想知道仿真器的速度控制。

我现在正在做的是假设我每秒获得60帧,我使用一个以下列方式触发1/60秒的计时器(伪代码):

timer_ticked()
{
    for(int i = 0; i < Settings.CyclesPerFrame; i++)
    {
        EmulateCycle();
    }

    if (IsDrawFlagSet)
    {
        DrawGraphics();
    }
}

我正在使用一个名为microtimer http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer的高分辨率计时器,我相信计时器不会等待timer_ticked完成以触发下一个周期(fon实例,创建一个新线程)而我是由于尝试使用GDI绘制到窗口(使用control.GetGraphics()方法),因此线程和表单存在问题似乎是线程安全的,但尝试创建SDLDotNet方法(仅作为示例)并不存在。

您认为哪种方法最好控制模拟器的速度而不会陷入计时器线程疯狂?

PS:您可以在GitHub中找到模拟器的源代码:https://github.com/AlFranco/C8POC

谢谢!

1 个答案:

答案 0 :(得分:0)

如果在完成之前再次调用tick方法,则问题不在于您的计时器。这是因为处理时间超过16.6毫秒。获得更好的计时器并不能解决您的问题。

也就是说,你可以通过几种方式防止重入。

您可以在输入回调时禁用计时器,并在完成后重新启用它。这将阻止多次调用:

timer_ticked()
{
    timer.Enabled = false;
    // do stuff here
    timer.Enabled = true;
}

请注意,这并不能为您提供完美的16.6毫秒滴答频率。相反,启用计时器后,下一个刻度将发生在16.6 ms(大约)。您的实际周期为16.6 ms加上处理所需的时间很长。

唯一一次失败的是,如果在下一个滴答发生之前没有调用timer_ticked方法。

如果您想保证无法获得并发滴答,可以使用System.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毫秒一次,因为第二个滴答基本上被忽略。你最好用一次性定时器交易。

另一种可能性是使用Stopwatch来计算处理程序所需的时间,并从下一个延迟期中减去该值:

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);

但是,如果您的处理程序经常花费的时间超过计时器延迟,那么这对您没有帮助。您要么必须降低计时器频率(即延迟更长时间),要么优化处理代码。