C#定时循环速率不准确

时间:2016-04-24 20:39:47

标签: c# .net timing

我正在尝试在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)下,输出速率更接近目标。我不明白为什么会这样。我的代码有什么问题?

2 个答案:

答案 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()?)。