簡體   English   中英

C#計時器分辨率:Linux(單點,dotnet核心)與Windows

[英]C# Timer resolution: Linux (mono, dotnet core) vs Windows

我需要一個每25毫秒觸發一次的計時器。 我一直在比較dotnet核心運行時和最新的單運行時在Windows 10和Linux(Ubuntu Server 16.10和12.04)之間的默認Timer實現。

我不太了解計時器精度方面的一些差異。

我正在使用以下代碼來測試Timer:

// inside Main()
        var s = new Stopwatch();
        var offsets = new List<long>();

        const int interval = 25;
        using (var t = new Timer((obj) =>
        {
            offsets.Add(s.ElapsedMilliseconds);
            s.Restart();
        }, null, 0, interval))
        {
            s.Start();
            Thread.Sleep(5000);
        }

        foreach(var n in offsets)
        {
            Console.WriteLine(n);
        }

        Console.WriteLine(offsets.Average(n => Math.Abs(interval - n)));

在Windows上到處都是:

...
36
25
36
26
36
5,8875 # <-- average timing error

在Linux上使用dotnet core,到處都是:

...
25
30
27
28
27
2.59776536312849 # <-- average timing error

但是mono Timer非常精確:

...
25
25
24
25
25
25
0.33 # <-- average timing error

編輯:即使在Windows上,單聲道仍保持其定時精度:

...
25
25
25
25
25
25
25
24
0.31

是什么造成了這種差異? 與mono相比,dotnet核心運行時做事的方式是否有好處,可以證明喪失精度?

不幸的是,您不能依賴.NET框架中的計時器。 最好的頻率為15毫秒,即使您想每毫秒觸發一次。 但是,您也可以實現具有微秒精度的高分辨率計時器。

注意:僅當Stopwatch.IsHighResolution返回true時,此方法才有效。 在Windows中,從Windows XP開始是正確的。 但是,我沒有測試其他框架。

public class HiResTimer
{
    // The number of ticks per one millisecond.
    private static readonly float tickFrequency = 1000f / Stopwatch.Frequency;

    public event EventHandler<HiResTimerElapsedEventArgs> Elapsed;

    private volatile float interval;
    private volatile bool isRunning;

    public HiResTimer() : this(1f)
    {
    }

    public HiResTimer(float interval)
    {
        if (interval < 0f || Single.IsNaN(interval))
            throw new ArgumentOutOfRangeException(nameof(interval));
        this.interval = interval;
    }

    // The interval in milliseconds. Fractions are allowed so 0.001 is one microsecond.
    public float Interval
    {
        get { return interval; }
        set
        {
            if (value < 0f || Single.IsNaN(value))
                throw new ArgumentOutOfRangeException(nameof(value));
            interval = value;
        }
    }

    public bool Enabled
    {
        set
        {
            if (value)
                Start();
            else
                Stop();
        }
        get { return isRunning; }
    }

    public void Start()
    {
        if (isRunning)
            return;

        isRunning = true;
        Thread thread = new Thread(ExecuteTimer);
        thread.Priority = ThreadPriority.Highest;
        thread.Start();
    }

    public void Stop()
    {
        isRunning = false;
    }

    private void ExecuteTimer()
    {
        float nextTrigger = 0f;

        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();

        while (isRunning)
        {
            nextTrigger += interval;
            float elapsed;

            while (true)
            {
                elapsed = ElapsedHiRes(stopwatch);
                float diff = nextTrigger - elapsed;
                if (diff <= 0f)
                    break;

                if (diff < 1f)
                    Thread.SpinWait(10);
                else if (diff < 5f)
                    Thread.SpinWait(100);
                else if (diff < 15f)
                    Thread.Sleep(1);
                else
                    Thread.Sleep(10);

                if (!isRunning)
                    return;
            }


            float delay = elapsed - nextTrigger;
            Elapsed?.Invoke(this, new HiResTimerElapsedEventArgs(delay));

            // restarting the timer in every hour to prevent precision problems
            if (stopwatch.Elapsed.TotalHours >= 1d)
            {
                stopwatch.Restart();
                nextTrigger = 0f;
            }
        }

        stopwatch.Stop();
    }

    private static float ElapsedHiRes(Stopwatch stopwatch)
    {
        return stopwatch.ElapsedTicks * tickFrequency;
    }
}

public class HiResTimerElapsedEventArgs : EventArgs
{
    public float Delay { get; }

    internal HiResTimerElapsedEventArgs(float delay)
    {
        Delay = delay;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM