我正在尝试在C#中创建一个方法,我可以以目标速率重复执行一个操作(在我的特定应用程序中它正在发送UDP数据包)。我知道C#中的定时器不准确会阻止输出精确地达到目标速率,但是尽力而为。然而,在更高的利率,时间似乎完全关闭。
while (!token.IsCancellationRequested)
{
stopwatch.Restart();
Thread.Sleep(5); // Simulate process
int processTime = (int)stopwatch.ElapsedMilliseconds;
int waitTime = taskInterval - processTime;
Task.Delay(Math.Max(0, waitTime)).Wait();
}
有关完整的示例控制台应用,请参阅here。
运行时,此测试应用程序的FPS输出显示大约44-46 Hz,目标为60 Hz。然而,在较低的速率(例如20Hz)下,输出速率更接近目标。我不明白为什么会这样。我的代码有什么问题?
答案 0 :(得分:2)
问题是Thread.Sleep
(或Task.Delay
)不是很准确。看看这个:Accuracy of Task.Delay
解决此问题的一种方法是启动计时器一次,然后进行循环,在每次迭代中延迟约15毫秒。在每次迭代中,您可以计算到目前为止应该执行操作的次数,并将其与到目前为止运行它的次数进行比较。然后你运行足够的操作来赶上。
以下是一些代码示例:
private static void timerTask(CancellationToken token)
{
const int taskRateHz = 60;
var stopwatch = new Stopwatch();
stopwatch.Start();
int ran_so_far = 0;
while (!token.IsCancellationRequested)
{
Thread.Sleep(15);
int should_have_run =
stopwatch.ElapsedMilliseconds * taskRateHz / 1000;
int need_to_run_now = should_have_run - ran_so_far;
if(need_to_run_now > 0)
{
for(int i = 0; i < need_to_run_now; i++)
{
ExecuteTheOperationHere();
}
ran_so_far += need_to_run_now;
}
}
}
请注意,如果流程要保持很长时间,请使用long
而不是int
。
答案 1 :(得分:1)
如果你替换它:
Task.Delay(Math.Max(0, waitTime)).Wait();
用这个:
Thread.Sleep(Math.Max(0, waitTime));
你应该接近更高价格的价值(为什么你会使用Task.Delay(..).Wait()
?)。