Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/265.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 仿真器中的线程和计时器_C#_.net_Multithreading_Gdi_Emulation - Fatal编程技术网

C# 仿真器中的线程和计时器

C# 仿真器中的线程和计时器,c#,.net,multithreading,gdi,emulation,C#,.net,Multithreading,Gdi,Emulation,我正在用C#开发一个Chip-8仿真器,我已经完成了几乎所有方面的部分工作,但是我仍然对仿真器的速度控制感到疑惑 我现在做的是假设我每秒得到60帧,我使用一个计时器,以以下方式触发1/60秒(伪代码): timer_ticked() { 对于(int i=0;i

我正在用C#开发一个Chip-8仿真器,我已经完成了几乎所有方面的部分工作,但是我仍然对仿真器的速度控制感到疑惑

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

timer_ticked()
{
对于(int i=0;i
我使用的是一个名为microtimer的高分辨率计时器,我相信计时器不会等到timer_______________________________________________________,但尝试创建SDLDotNet方法(只是一个示例)并没有成功

您认为哪种方法最适合控制仿真器的速度而不陷入计时器线程疯狂状态

PS:您可以在GitHub中找到仿真器的源代码:


谢谢大家!

如果勾选方法在完成之前再次被调用,问题不在于计时器。这是因为处理时间比16.6毫秒要长。使用更好的计时器并不能解决问题

也就是说,您可以通过两种方式防止重入

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

timer_ticked()
{
    timer.Enabled = false;
    // do stuff here
    timer.Enabled = true;
}
请注意,这并没有给你一个完美的16.6毫秒滴答频率。相反,下一个滴答声将在启用计时器后16.6毫秒(大约)出现。您的实际周期为16.6毫秒,再加上处理所需的时间

唯一失败的时间是在下一次勾选之前没有调用
timer\u 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);
}
编辑 如果处理程序仍在处理前一个勾号,则无法轻松告诉计时器不发出勾号。但是,您可以阻止计时器勾号处理程序对随后的勾号执行任何操作。您只需使用
监视器

private object timerLock = new object();
timer_ticked()
{
    if (!Monitor.TryEnter(timerLock))
        return;
    try
    {
        // do stuff here
    }
    finally
    {
        Monitor.Exit(timerLock);
    }
}
这种解决方案的问题是,如果计时器设置为16毫秒,处理程序需要17毫秒,那么有效更新率将是每32毫秒一次,因为第二个刻度基本上被忽略。你最好是一次性交易

另一种可能是使用秒表来计时处理程序所需的时间,并从下一个延迟周期中减去:

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

但是,同样,如果处理程序通常需要比计时器延迟更长的时间,那么这对您没有帮助。您必须降低计时器频率(即延长延迟)或优化处理代码。

伪代码不能保证每秒60帧,因为您受操作系统和处理器本身的支配。如果
GetGraphics()
实际上是线程安全的,我会非常震惊。谢谢你的回答!那么16,6ms对于一个正常的计时器来说太精确了?你知道更好的解决办法吗?我明白你的意思。是否有更好的方法在仿真中模拟处理器的滴答声?根据我的想法,我剩下的唯一想法是实现一个计时器,只有在前一个勾号函数完成时,才会每隔x毫秒触发一次,如果它正在等待,则会立即触发。再说一次,我不喜欢上面的方法,我认为如果你想到其他方法,最好讨论一下:)。非常感谢。
var delayTime = Math.Max(0, 16 - sw.ElapsedMilliseconds);
timer.Change(delayTime, -1);