.net中的高精度休眠/等待

High-precision sleeping / waiting in .net

背景:我用 F#/.net 编写了一个 gameboy 模拟器。在 gameboy 中,vblank (~fps) 约为 60 Hz,并且与游戏速度相关,因此模拟器 运行s 尽可能接近 60 fps 非常重要。

由于现代计算机可以 运行 我的模拟器比 60 fps 快很多,所以我需要降低模拟器的速度。我目前的做法是计算两个 VBlank 之间的时间,然后等待该 vblank 周期的剩余时间。

问题在于如何在不忙于循环 cpu 的情况下等待。由于我通常需要等待几毫秒(有时更长,有时更少),内置的 Thread.sleep 函数不是一个好的选择,因为除非您指定 0 等待时间,否则它至少会休眠 ~15 毫秒,这太长了(而且不准确)。我当前的方法是使用 sleep(0),它实际上只是一个奇特的自旋锁(其他线程可能 运行,但您仍然会最大化 cpu)。

解决这个问题的正确方法是什么?我正在考虑等待从计时器释放的信号量,但是计时器可以提供所需的时间分辨率吗?无论如何,这不只是一个美妙的睡眠吗?

编辑:这被标记为 What Thread sleep method is most precise: Monitor.Wait vs System.Timer vs DispatchTimer vs Threading.Timer 的副本,但我认为这不是精度问题,更多的是为紧凑的游戏循环找到合适的解决方案。

正如您所指出的,默认系统计时器不够快 - 15.6 毫秒能够达到 60 FPS,但不能达到 "constant time per frame" 或接近于此的任何水平。

一种解决方案是使用忙循环,但是,对于如此长的等待时间,这是一种巨大的浪费(有趣的是一种方法是 "way too short" 而另一种是 "way too long" :) ).

另一种选择是使用 timeBeginPeriod (https://msdn.microsoft.com/en-us/library/windows/apps/dd757624(v=vs.85).aspx) 更改系统计时器 - 您需要使用 P/Invokes 来访问此 API,但它将让您更准确地入睡。或者更好,使用 Timer 设置为 ~16.7ms。

如果你不想弄乱它,只需制作一个计时器(System.Threading.Timer,而不是 windows 形式的计时器)并将其设置为 15 毫秒。虽然这不会为您提供准确的 60 FPS,但它应该平均每秒更新约 64 次,这应该足够接近以至于不易察觉。由于计时器回调与 "sleep" 分离(没有真正的睡眠,但您在等待计时器触发时没有做 CPU 工作),它不会倾向于跳过或加倍帧windows 形成一个。请注意,这隐含地涉及多线程,因此请确保正确使用同步。