如何在没有CPU过载的情况下在任务中引入准确的小延迟?

YRo*_*Rod 8 .net c# multithreading task winforms

我正在实现一种通信算法来定期和非常快速地发送信息,即包之间的1ms.我有一个使用Tasks发送包的功能版本.这是我的代码示例:

private void Work()
{
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    while (!cancellationTokenSource.Token.IsCancellationRequested)
    {
        if (!Pack.PeriodicOn)
            cancellationTokenSource.Cancel();

        // Time used to send the packs before the interval time
        double tolerance = Pack.Interval * 0.2F;

        // In case of interval bigger than 25ms send pasks only 5ms before
        if (tolerance > 5) tolerance = 5;

        TimeSpan timeSpan = stopwatch.Elapsed;

        // The waiting time is controlled by the condition below, if the condition is false, the while loop continues execution         
        // Send the information a little bit before to interval time to deal with the transmision delay
        if (Pack.LastSent.TotalMilliseconds == 0 ||
             timeSpan.TotalMilliseconds - Pack.LastSent.TotalMilliseconds >=
             (Pack.Interval - tolerance))
        {
            SendData(Pack);
            Pack.LastSent = timeSpan;
        }
    }

    Pack.LastSent = new TimeSpan(0);
}
Run Code Online (Sandbox Code Playgroud)

我的问题依赖于CPU使用率增加到不合需要的水平.我知道我可以通过引入一些延迟来避免这种情况,但是,Thread.Sleep(1)是非常不准确的,并且包之间的实际传输间隔上升,如果我使用await Task.Delay(1)似乎产生相同的效果.

有没有人有另一种方法来准确地推迟任务?

提前致谢!

Mik*_*ray 1

Windows 不是实时操作系统,因此不能保证计时器准确工作。典型的系统时钟精度约为 15 毫秒。然而,可以获得比标准更准确的事件System.Threading.Timer。Windows API 具有专为多媒体场景设计的计时器,可以以更精确的时间间隔触发。我已经更新了我维护的 GitHub 存储库HighPrecisionTimer中的代码,该存储库利用该 API 来包含基于任务的MultimediaTimer.Delay方法:

private static async Task RunEveryMillisecond(CancellationToken token)
{
    Stopwatch s = Stopwatch.StartNew();
    TimeSpan prevValue = TimeSpan.Zero;
    int i = 0;
    while (true)
    {
        Console.WriteLine(s.ElapsedMilliseconds);
        await MultimediaTimer.Delay(1, token);
        if (Console.KeyAvailable)
        {
            return;
        }

        i++;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,此事件在我的系统上大约每 1.5 毫秒触发一次,并且在运行时仍会占用 CPU 约 10%,因此它对系统资源的影响不可忽略。项目中包含的标准计时器方法对于在 1 ms 级别上运行方法来说更加准确和高效(CPU 开销更少,约 1%)。我的猜测是,基于任务的延迟方法中有更多的分配和垃圾收集开销。

请注意,使用此 API 可能会产生副作用,例如电池寿命缩短。然而,它对于需要更短时间的测试类型场景很有用。