Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/meteor/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将秒表和计时器结合起来,以获得具有平滑强制的长期准确事件_C#_Timer_Stopwatch - Fatal编程技术网

C# 将秒表和计时器结合起来,以获得具有平滑强制的长期准确事件

C# 将秒表和计时器结合起来,以获得具有平滑强制的长期准确事件,c#,timer,stopwatch,C#,Timer,Stopwatch,首先,我要说的是,我理解Windows不是一个实时操作系统。我不希望任何给定的计时器间隔精确到15毫秒以上。但是,从长远来看,计时器滴答声的总和精确对我来说很重要,这在库存计时器或秒表中是不可能的 如果一个普通的System.Timers.Timer(或者更糟糕的是,一个Windows.Forms.Timer)被配置为每100毫秒运行一次,它的滴答声在100毫秒到115毫秒之间。这意味着如果我需要做一天的活动,10*60*60*24=864000次,我可能会迟到三个多小时!这对我的申请是不可接受

首先,我要说的是,我理解Windows不是一个实时操作系统。我不希望任何给定的计时器间隔精确到15毫秒以上。但是,从长远来看,计时器滴答声的总和精确对我来说很重要,这在库存计时器或秒表中是不可能的

如果一个普通的System.Timers.Timer(或者更糟糕的是,一个Windows.Forms.Timer)被配置为每100毫秒运行一次,它的滴答声在100毫秒到115毫秒之间。这意味着如果我需要做一天的活动,10*60*60*24=864000次,我可能会迟到三个多小时!这对我的申请是不可接受的

如何最好地构造一个平均持续时间准确的计时器?我已经建立了一个,但我认为它可以更平滑,更有效,或者…必须有一个更好的方法。为什么.NET会提供一个低精度的日期时间。现在,一个低精度的计时器和一个高精度的诊断。秒表,但没有高精度的计时器

我编写了以下计时器更新例程:

var CorrectiveStopwatch = new System.Diagnostics.Stopwatch();
var CorrectedTimer = new System.Timers.Timer()
{
    Interval = targetInterval,
    AutoReset = true,
};
CorrectedTimer.Elapsed += (o, e) =>
{
    var actualMilliseconds = ; 

    // Adjust the next tick so that it's accurate
    // EG: Stopwatch says we're at 2015 ms, we should be at 2000 ms
    // 2000 + 100 - 2015 = 85 and should trigger at the right time
    var newInterval = StopwatchCorrectedElapsedMilliseconds + 
                      targetInterval -
                       CorrectiveStopwatch.ElapsedMilliseconds;

    // If we're over 1 target interval too slow, trigger ASAP!
    if (newInterval <= 0)
    {
        newInterval = 1; 
    }

    CorrectedTimer.Interval = newInterval;

    StopwatchCorrectedElapsedMilliseconds += targetInterval;
};
这个序列的平均值是,我知道网络时间和其他工具会平滑地强制当前时间以获得准确的计时器。也许执行某种PID循环以将目标间隔设置为targetInterval-15/2左右可能需要较小的更新,或者平滑可以减少短事件和长事件之间的时间差。我如何将其与我的方法相结合?有没有这样做的现有库

我把它和普通的秒表相比,它似乎是准确的。5分钟后,我测量了以下毫秒计数器值:

DateTime elapsed:  300119
Stopwatch elapsed: 300131
Timer sum:         274800
Corrected sum:     300200
30分钟后,我测量:

DateTime elapsed:  1800208.2664
Stopwatch elapsed: 1800217
Timer sum:         1648500
Corrected sum:     1800300
库存计时器比秒表慢151.8秒,占总数的8.4%。这似乎非常接近平均滴答持续时间在100到115毫秒之间!此外,校正后的总和仍然在测量值的100ms范围内(这些值比我用手表发现的误差更准确),因此这是可行的

我的完整测试程序如下。它在Windows窗体中运行,需要“Form1”上的文本框,或者删除
textBox1.text=
行并在命令行项目中运行它

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace StopwatchTimerTest
{
    public partial class Form1 : Form
    {
        private System.Diagnostics.Stopwatch ElapsedTimeStopwatch;
        private double TimerSumElapsedMilliseconds;
        private double StopwatchCorrectedElapsedMilliseconds;
        private DateTime StartTime;

        public Form1()
        {
            InitializeComponent();
            Run();
        }

        private void Run()
        {
            var targetInterval = 100;
            // A stopwatch running in normal mode provides the correct elapsed time to the best abilities of the system
            // (save, for example, a network time server or GPS peripheral)
            ElapsedTimeStopwatch = new System.Diagnostics.Stopwatch();

            // A normal System.Timers.Timer ticks somewhere between 100 ms and 115ms, quickly becoming inaccurate.
            var NormalTimer = new System.Timers.Timer()
            {
                Interval = targetInterval,
                AutoReset = true,
            };
            NormalTimer.Elapsed += (o, e) =>
            {
                TimerSumElapsedMilliseconds += NormalTimer.Interval;
            };

            // This is my "quick and dirty" corrective stopwatch
            var CorrectiveStopwatch = new System.Diagnostics.Stopwatch();
            var CorrectedTimer = new System.Timers.Timer()
            {
                Interval = targetInterval,
                AutoReset = true,
            };
            CorrectedTimer.Elapsed += (o, e) =>
            {
                var actualMilliseconds = CorrectiveStopwatch.ElapsedMilliseconds; // Drop this into a variable to avoid confusion in the debugger

                // Adjust the next tick so that it's accurate
                // EG: Stopwatch says we're at 2015 ms, we should be at 2000 ms, 2000 + 100 - 2015 = 85 and should trigger at the right time
                var newInterval = StopwatchCorrectedElapsedMilliseconds + targetInterval - actualMilliseconds;

                if (newInterval <= 0)
                {
                    newInterval = 1; // Basically trigger instantly in an attempt to catch up; could be smoother...
                }
                CorrectedTimer.Interval = newInterval;

                // Note: could be more accurate with actual interval, but actual app uses sum of tick events for many things
                StopwatchCorrectedElapsedMilliseconds += targetInterval;
            };

            var updateTimer = new System.Windows.Forms.Timer()
            {
                Interval = 1000,
                Enabled = true,
            };
            updateTimer.Tick += (o, e) =>
            {
                var result = "DateTime elapsed:  " + (DateTime.Now - StartTime).TotalMilliseconds.ToString() + Environment.NewLine + 
                             "Stopwatch elapsed: " + ElapsedTimeStopwatch.ElapsedMilliseconds.ToString() + Environment.NewLine +
                             "Timer sum:         " + TimerSumElapsedMilliseconds.ToString() + Environment.NewLine +
                             "Corrected sum:     " + StopwatchCorrectedElapsedMilliseconds.ToString();
                Console.WriteLine(result + Environment.NewLine);
                textBox1.Text = result;
            };

            // Start everything simultaneously
            StartTime = DateTime.Now;
            ElapsedTimeStopwatch.Start();
            updateTimer.Start();
            NormalTimer.Start();
            CorrectedTimer.Start();
            CorrectiveStopwatch.Start();
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用系统数据;
使用系统图;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows.Forms;
命名空间StopwatchTimerTest
{
公共部分类Form1:Form
{
专用系统.Diagnostics.Stopwatch ElapsedTimeStopwatch;
专用双定时器SumerFassedMilliseconds;
私人双秒表校正过的延时;
私人日期时间开始时间;
公共表格1()
{
初始化组件();
Run();
}
私家车
{
var targetInterval=100;
//在正常模式下运行的秒表为系统的最佳性能提供正确的运行时间
//(例如,保存网络时间服务器或GPS外围设备)
ElapsedTimeStopwatch=新系统.Diagnostics.Stopwatch();
//正常的System.Timers.Timer滴答声介于100毫秒和115毫秒之间,很快就会变得不准确。
var NormalTimer=new System.Timers.Timer()
{
间隔=目标间隔,
自动重置=真,
};
正常计时器。已用时间+=(o,e)=>
{
TimerSumeReleastDmilliseConds+=正常计时器时间间隔;
};
//这是我的“又快又脏”修正秒表
var CorrectiveStopwatch=new System.Diagnostics.Stopwatch();
var CorrectedTimer=new System.Timers.Timer()
{
间隔=目标间隔,
自动重置=真,
};
已更正的已用时间+=(o,e)=>
{
var actualMillSeconds=CorrectiveStopwatch.elapsedmillSeconds;//将其放入变量中以避免在调试器中混淆
//调整下一个勾号,使其准确
//秒表显示我们在2015毫秒,我们应该在2000毫秒,2000+100-2015=85,应该在正确的时间触发
var newInterval=秒表修正的延时Dmilliseconds+targetInterval-实际毫秒;
如果(新间隔
{
var result=“DateTime appeased:”+(DateTime.Now-StartTime).totalmillizes.ToString()+Environment.NewLine+
“秒表已过:”+ElapsedTimeStopwatch.ElapsedMilliseconds.ToString()+Environment.NewLine+
“Timer sum:”+timersumeReleastDmilliseconds.ToString()+Environment.NewLine+
“修正总和:”+StopWatch CorrectedDeleaseDmilliseConds.ToString();
Console.WriteLine(result+Environment.NewLine);
textBox1.Text=结果;
};
//同时启动一切
StartTime=DateTime.Now;
ElapsedTimeStopwatch.Start();
updateTimer.Start();
NormalTimer.Start();
已更正timer.Start();
正确的秒表。开始();
}
}
}
Windows不是实时操作系统

更准确地说,在受保护模式下按需分页虚拟内存操作系统的环3中运行的代码不能假定它可以提供任何执行保证。像页面错误这样简单的事情可以极大地延迟代码执行。更类似的是,第2代垃圾回收总是会破坏您的预期。环0代码(驱动程序)通常有很好的中断响应时间,模块化了一个糟糕的视频驱动程序,它在基准测试上作弊

所以不,希望代码激活了b
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace StopwatchTimerTest
{
    public partial class Form1 : Form
    {
        private System.Diagnostics.Stopwatch ElapsedTimeStopwatch;
        private double TimerSumElapsedMilliseconds;
        private double StopwatchCorrectedElapsedMilliseconds;
        private DateTime StartTime;

        public Form1()
        {
            InitializeComponent();
            Run();
        }

        private void Run()
        {
            var targetInterval = 100;
            // A stopwatch running in normal mode provides the correct elapsed time to the best abilities of the system
            // (save, for example, a network time server or GPS peripheral)
            ElapsedTimeStopwatch = new System.Diagnostics.Stopwatch();

            // A normal System.Timers.Timer ticks somewhere between 100 ms and 115ms, quickly becoming inaccurate.
            var NormalTimer = new System.Timers.Timer()
            {
                Interval = targetInterval,
                AutoReset = true,
            };
            NormalTimer.Elapsed += (o, e) =>
            {
                TimerSumElapsedMilliseconds += NormalTimer.Interval;
            };

            // This is my "quick and dirty" corrective stopwatch
            var CorrectiveStopwatch = new System.Diagnostics.Stopwatch();
            var CorrectedTimer = new System.Timers.Timer()
            {
                Interval = targetInterval,
                AutoReset = true,
            };
            CorrectedTimer.Elapsed += (o, e) =>
            {
                var actualMilliseconds = CorrectiveStopwatch.ElapsedMilliseconds; // Drop this into a variable to avoid confusion in the debugger

                // Adjust the next tick so that it's accurate
                // EG: Stopwatch says we're at 2015 ms, we should be at 2000 ms, 2000 + 100 - 2015 = 85 and should trigger at the right time
                var newInterval = StopwatchCorrectedElapsedMilliseconds + targetInterval - actualMilliseconds;

                if (newInterval <= 0)
                {
                    newInterval = 1; // Basically trigger instantly in an attempt to catch up; could be smoother...
                }
                CorrectedTimer.Interval = newInterval;

                // Note: could be more accurate with actual interval, but actual app uses sum of tick events for many things
                StopwatchCorrectedElapsedMilliseconds += targetInterval;
            };

            var updateTimer = new System.Windows.Forms.Timer()
            {
                Interval = 1000,
                Enabled = true,
            };
            updateTimer.Tick += (o, e) =>
            {
                var result = "DateTime elapsed:  " + (DateTime.Now - StartTime).TotalMilliseconds.ToString() + Environment.NewLine + 
                             "Stopwatch elapsed: " + ElapsedTimeStopwatch.ElapsedMilliseconds.ToString() + Environment.NewLine +
                             "Timer sum:         " + TimerSumElapsedMilliseconds.ToString() + Environment.NewLine +
                             "Corrected sum:     " + StopwatchCorrectedElapsedMilliseconds.ToString();
                Console.WriteLine(result + Environment.NewLine);
                textBox1.Text = result;
            };

            // Start everything simultaneously
            StartTime = DateTime.Now;
            ElapsedTimeStopwatch.Start();
            updateTimer.Start();
            NormalTimer.Start();
            CorrectedTimer.Start();
            CorrectiveStopwatch.Start();
        }
    }
}