使用AutoResetEvent创建迭代低于1ms的循环.等一下TimeSpan

本文关键字:TimeSpan 一下 循环 创建 AutoResetEvent 迭代 1ms 使用 | 更新日期: 2023-09-27 18:05:32

我正在尝试创建一个间隔小于1ms的循环,通过使用具有8000个节拍的TimeSpan启动(1个节拍= 100ns,因此8000个节拍等于0.8ms或800us):

    private static void MeasureAutoResetEvent()
    {
        TimeSpan interval = new TimeSpan(8000L); // 800us
        double elapsed = 0;
        Stopwatch watch = new Stopwatch();
        AutoResetEvent autoResetEvent = new AutoResetEvent(false);
        while (true)
        {
            watch.Restart();
            autoResetEvent.WaitOne(interval);
            watch.Stop();
            elapsed = ResolveTicks(UoM.Microsecond, watch.ElapsedTicks);
            Console.WriteLine(elapsed); // <- Does not writes ±800
        }
    }

UoM enum和ResolveTicks()方法定义如下:

    public enum UoM
    {
        Second,
        Millisecond,
        Microsecond,
        Nanosecond,
    }
    public static double ResolveTicks(UoM uom, long ticks)
    {
        return
            uom == UoM.Millisecond ? ticks * 1e3 / Stopwatch.Frequency :
            uom == UoM.Microsecond ? ticks * 1e6 / Stopwatch.Frequency :
            uom == UoM.Nanosecond ? ticks * 1e9 / Stopwatch.Frequency :
            ticks * 1 / Stopwatch.Frequency;
    }

我使用Stopwatch来测量autoResetEvent.WaitOne(interval);使用的时间。但是,控制台的输出不写±800。有人知道我上面的代码有什么问题吗?

使用AutoResetEvent创建迭代低于1ms的循环.等一下TimeSpan

这里的问题是.WaitOne()只精确到5到20毫秒之间(一般来说)。

当然,如果你想等800秒,那就差远了。

.WaitOne()的分辨率由当前Windows定时器分辨率决定

对于日常使用来说,计时器足够精确,但不是完全精确。当您进入毫秒级(或者更糟,低于毫秒级)时,任务切换机制就变得有影响力了。任务切换还取决于平台(Windows上是轮询,Linux上有几个轮询,具体取决于内核)。有些Linux内核是专门为RT(实时)应用程序编译的,可以保证特定的响应时间(取决于硬件)。

假设你的代码是正确的,并且你在Windows上运行,你可以尝试将进程优先级设置为最高级别(实时)并固定核心,以便它总是在同一个核心上运行(以便各种L1, L2, L3 CPU缓存被正确预热)。否则,您会陷入由任务切换引起的抖动。