4

DotNet 替代方案(MONO- http://www.go-mono.com/mono-downloads/download.html)正确处理短计时器条件(低至约 12 ms 触发时间)然而,DotNet 似乎需要比预期更长的时间,并且“错过”。那么,哪种行为是正确的呢?单声道是否正确跟踪其事件触发?它是否用不正确的秒表捏造了它的数字?还是 DotNet 有一个“惰性”事件计时器?通过我的测试,很明显,这不是一个潜在的 Windows 问题。

.NET 200ms+ Interval minimum

Mono 12ms+ Interval minimum

此查询与此类似:Timer take 10 ms more than interval

但是考虑代码:

在 .NET 中,错过的事件会慢慢增加:

Total Runtime/Interval - Actual Events = Missed Events

代码:

class Program
{
    const int Interval = 60; //ms Event fireing

    Stopwatch TotalRunTime = new Stopwatch();//Full runtime.
    Stopwatch Calctime = new Stopwatch(); //Used to load up the Event to 66 percent

    System.Timers.Timer TIMER; //Fireing Event handler.
    int TotalCycles = 0; //Number of times the event is fired.
    int Calcs_per_cycle = 100; // Number of calcs per Event.

    static void Main(string[] args)
    {
        Program P = new Program();
        P.MainLoop();
    }

    void MainLoop()
    {
        Thread.CurrentThread.Priority = ThreadPriority.Highest;

        TIMER = new Timers.Timer();
        TIMER.Interval = Interval;
        TIMER.Elapsed += new ElapsedEventHandler(MS_Calc);
        TIMER.AutoReset = true;
        TIMER.Enabled = true; //Start Event Timer
        TotalRunTime.Start(); //Start Total Time Stopwatch;

        while (true)
        {
            Thread.Sleep(Interval * 5);
            Console.Clear();
            PrintAtPos(2, "Missed Events " + (((TotalRunTime.ElapsedMilliseconds / Interval) - TotalCycles).ToString()));

        }

    }
    public void MS_Calc(object source, System.Timers.ElapsedEventArgs E)// public void MS_Calc(object source, ElapsedEventArgs E)
    {
        TotalCycles++;
        Calctime.Start();
        for (int i = 0; i < Calcs_per_cycle; i++)
        {
            int A = 2;
            int B = 2;
            int c = A + B;
        }
        PrintAtPos(1, "Garbge Collections G1: " + GC.CollectionCount(0) + "  G2: " + GC.CollectionCount(1) + "  G3: " + GC.CollectionCount(2) + " ");
        Calctime.Stop();
        if (Interval * 0.667 > Calctime.ElapsedMilliseconds) //only fill the even timer 2/3s 
        //full to make sure we finish before the next event
        {
            Calcs_per_cycle = (int)(Calcs_per_cycle * 1.035);
        }
        Calctime.Reset();
        PrintAtPos(0, "Calc Time : " + (DateTime.Now - E.SignalTime).Milliseconds + "  Calcs/Cycle: " + Calcs_per_cycle);
    }

    private static void PrintAtPos(int Vertical_Pos, string Entry)
    {
        Console.SetCursorPosition(0, Vertical_Pos);
        Console.Write(Entry);
    }
}
4

1 回答 1

4

问题是 Windows 系统时钟在您使用的时间尺度上并不精确,我相信微软的实现Timer是使用系统时钟来确定何时触发Elapsed事件。我假设这是因为当您考虑系统时钟的(不)准确性时,您可以获得对观察到的行为的相当准确的描述。

系统时钟只能精确到 15.6 毫秒(原因是为了保持电池寿命,请参阅问题)。因此,Elapsed事件并不是每 60 毫秒触发一次,而是每 62.4 毫秒触发一次。那 2.4 毫秒的差异聚合起来,你有“错过”的事件。我运行你的程序 33009 毫秒,报告了 22 个错过的事件。我会期待不同的33009 * (1/60 - 1/62.4) = 21.2事件。鉴于我们没有 15.6 毫秒的数字来达到更高的准确度,这与您将获得的差不多。(例如,如果经过的时间是 62.5 毫秒,您会得到 22.0 次预期未命中)。

您的示例肯定表明框架实现Timer 可能更准确。我不知道为什么 Mono 的实现更准确,但通常计时器不是精确的毫秒计时。还有其他比系统时钟更准确的计时方法,Stopwatch可用时由班级使用。

于 2012-12-20T06:11:43.787 回答