当前位置:  开发笔记 > 编程语言 > 正文

C#计时器分辨率:Linux(单点,dotnet核心)与Windows

如何解决《C#计时器分辨率:Linux(单点,dotnet核心)与Windows》经验,为你挑选了1个好方法。

我需要一个每25毫秒触发一次的计时器。我一直在比较Timerdotnet核心运行时和最新的单运行时在Windows 10和Linux(Ubuntu Server 16.10和12.04)之间的默认实现。

我不太了解计时器精度方面的一些差异。

我正在使用以下代码来测试Timer:

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

        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

但是单声道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核心运行时做事的方式是否有好处,可以证明丧失精度?



1> György Kősze..:

不幸的是,您不能依赖.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 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;
    }
}

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有