是定时器和循环在.net准确吗?(Are Timers and Loops in .Net accu

2019-06-17 11:05发布

同时开发一个程序来计算由555计时器所产生的脉冲的频率和脉冲宽度,来经由PC并行端口的PC。 我注意到,每一次我运行它显示了不同数值的代码,所以我开始测试循环和定时器,因为有准确性。 我已经运行下面的代码,并来到他们是不准确的点(我可能是错的,请纠正我,如果我在这里!):

对于定时器:

    int sec = 0;
    private void button2_Click(object sender, EventArgs e)
    {
        sec = DateTime.Now.Second;
        i = 0;
        timer1.Enabled = true;
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        if (sec == DateTime.Now.Second)
        {
            i++;
        }
        else
        {
            timer1.Enabled = false;
            MessageBox.Show(i.ToString(),"Timer Output");
        }
    }

OUTPUT:应该是一样的,但是:

FOR循环:

    private void button1_Click(object sender, EventArgs e)
    {
        i = 0;
        CheckForIllegalCrossThreadCalls = false;
        Thread t1 = new Thread(LoopTest);
        t1.Start();
    }

    void LoopTest()
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        this.Cursor = Cursors.WaitCursor;
        while (true)
        {
            if (sw.ElapsedMilliseconds != 1000)
            {
                i++;
            }
            else
            {
                break;
            }
        }
        sw.Stop();
        this.Cursor = Cursors.Default;
        MessageBox.Show(i.ToString(), "Loop Output");
    }

OUTPUT:应该是一样的,但是:

我应该怎么做才能让循环和计时准确,有没有办法做到这一点? 或者我必须去艰苦和复杂的C代码和DOS?

我认为这是他们让我在这个问题上错误的价值观的根本原因: 计数并行端口输入频率- C#

Answer 1:

1)不要使用DateTime.Now的性能测量,使用StopWatch

2) “ OUTPUT: Should be same, but ..

他们为什么要? 您运行管理上非RTOS(实时操作系统)/ JIT'ed代码。 你的代码可以在任何时间,如果操作系统感觉就像是获得反弹。 是什么让你相信,在这个环境中运行相同的代码N次应该总是产生这么小的程度相同的结果?

3)在Windows定时器有一个15毫秒〜分辨率。 非常准确的定时最好的办法是在系统(CPU的),它支持它的HighPerformanceTimer API。 您还没有向我们展示了定时器的时间间隔。

你不能在这里考虑许多变量和你在错误的假设基础的预测。 有多少次你甚至测量这个代码? 你是否考虑到编译它通过第一次所需的时间? 你是否在释放模式运行? 通过VS? 有没有在后台运行多个任务? 我可以继续下去。



Answer 2:

认真实施允许在大多数Windows平台的时间段的测量下降到只有几微秒的精度。 请注意以下事实:

  1. Windows不是一个实时操作系统:这个无所谓了!

  2. 利用进程/线程优先级: SetPriorityClass高达REALTIME_PRIORITY_CLASS和SetThreadPriority函数高达THREAD_PRIORITY_TIME_CRITICAL 。 确保有安全的代码,因为当调用线程忙于这些重点可锁定系统。 (Process.PriorityClass和Thread.Priority不太能够筹集到所需级别的优先级。)

  3. 可能增加系统借助于中断周期和时间的更新间隔多媒体计时器 。 (有各种.NET项目来封装这些多媒体定时器功能。)

  4. 在多核系统的核心的选择也会影响精度。 这是advantagous强行线程等待计时器事件上等待Processor0。 该SetThreadAffinityMask功能允许一个线程绑定到特定的CPU。 (.NET应用程序见Thread.ProcessorAffinity 。)

  5. 使用QueryPerformanceCounter的和QueryPerformanceFrequency的作为rescources高频时间的措施。 (.NET应用程序,请参阅: 创建QueryPerfCounter包装类 )

  6. 确保性能计数器的频率值进行校准。 返回的值QueryPerformanceFrequency从由所观察到的值的偏移量,并通过一些热漂移偏差。 这可能/将介绍我们很多人/秒的误差。 请参阅的Windows时间戳项目 ,以了解如何这样的校准可以做到的。

和:是的,你可能要为一些硬编码。 但是微秒时间可以观察到非常可靠的Windows平台上。

注意:Windows不是一个实时操作系统。 但是,在Windows上使用的定时器是非常精确的。 他们做他们应该做什么和在非常高的精度做到这一点。 有很多有关Windows定时器和其准确性投诉的事实是,他们的行为依赖于底层硬件上不少。 这也是为什么该文件有许多缺陷的原因。 强烈建议诊断硬件,以便找到个人计时服务能力。 不幸的是这会导致任何程序有一些代码行额外独立于平台。



Answer 3:

首先,你不知道时间是怎么远到当前第二,当你button2_click被调用。 因此,它基本上是随机当显示MessageBox的多少秒的离开 - 这是你所看到的。

其次,一个循环的时间跨度内有多少个CPU周期变得无关准确。

多少次任意给定的线程得到取决于什么其他的系统是怎么回事。 如果系统决定周期的一大堆需要去到另一个过程中,你的线程会被“饿死”了一段时间。

也许你可以详细说一下你真正想要做的,有人可以提供一些建议。



Answer 4:

埃德的答案是正确的:在Windows上的计时器是不准确的。 嗯,当然不够准确测量硬件产生的信号。

但是,如果我有测量频率和脉冲宽度,我会带百样左右,取它们的平均值。 也许像这样:

private StopWatch _Sw = new StopWatch();
private List<TimeSpan> _Samples = new List<TimeSpan>();
private Timer _Timer = new Timer(TimerTick, TimeSpan.FromSeconds(1));
private const int RequiredSamples = 100;

private void StartSampling()
{
    // You can change this next line to PriorityClass.RealTime if you're careful.
    System.Diagnostics.Process.GetCurrentProcess().BasePriority 
                              = PriorityClass.High;
    _Samples.Capacity = RequiredSamples;
    _Timer.Start();
    _Sw.Start();
    Hook555Timer(On555Pulse);
}

private void On555Pulse(object sender, EventArgs e)
{
    _Sample.Add(_Sw.Elapsed);
}

private void TimerTick(object sender, EventArgs e)
{
    if (_Samples.Count > RequiredSamples)
    {
        System.Diagnostics.Process.GetCurrentProcess().BasePriority 
                                    = PriorityClass.Normal;
        _Timer.Stop();
        _Sw.Stop();
        UnHook555Timer(On555Pulse);

        // You can now use the time between each TimeSpan 
        // in _Samples to determine statistics about your timer.
        // Eg: Min / Max duration, average and median duration.
        var durations = _Samples
                .Zip(_Samples.Skip(1), 
                    (a,b) => new { First = a, Second = b } )
                .Select(pair => pair.Second.Subtract(pair.First));
        var minTime = durations.Min(ts => ts.TotalMilliseconds);
        var maxTime = durations.Max(ts => ts.TotalMilliseconds);
        var averageTime = durations.Average(ts => ts.TotalMilliseconds);
        // I don't think LINQ has a Median() aggregate out of the box.
        // Some comment about "an exercise for the reader" goes here.
        var medianTime = durations.Median(ts => ts.TotalMilliseconds);

        var frequency = _Samples.Last()
                                 .Subtract(_Samples.First())
                                      .TotalSeconds / _Samples.Count;
    }
}

(注:写在记事本的代码,不可能没有进一步的修改工作)

我现在的精度由以下因素决定StopWatch而不是Timer (请注意, Stopwatch可能不会比一个更精确的Timer在系统上,检查FrequencyIsHighResolution属性)。 而我采样,以尽量减少其他进程抢占我的过程中,增加了进程的优先级。

但即便如此,因为Windows是不是一个RTOS,你能做的最好的是需要大量的样本,并使用一些统计数据来得到答案的近似值。



文章来源: Are Timers and Loops in .Net accurate?