同时开发一个程序来计算由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#
1)不要使用DateTime.Now
的性能测量,使用StopWatch
。
2) “ OUTPUT: Should be same, but ..
”
他们为什么要? 您运行管理上非RTOS(实时操作系统)/ JIT'ed代码。 你的代码可以在任何时间,如果操作系统感觉就像是获得反弹。 是什么让你相信,在这个环境中运行相同的代码N次应该总是产生这么小的程度相同的结果?
3)在Windows定时器有一个15毫秒〜分辨率。 非常准确的定时最好的办法是在系统(CPU的),它支持它的HighPerformanceTimer API。 您还没有向我们展示了定时器的时间间隔。
你不能在这里考虑许多变量和你在错误的假设基础的预测。 有多少次你甚至测量这个代码? 你是否考虑到编译它通过第一次所需的时间? 你是否在释放模式运行? 通过VS? 有没有在后台运行多个任务? 我可以继续下去。
认真实施允许在大多数Windows平台的时间段的测量下降到只有几微秒的精度。 请注意以下事实:
Windows不是一个实时操作系统:这个无所谓了!
利用进程/线程优先级: SetPriorityClass高达REALTIME_PRIORITY_CLASS
和SetThreadPriority函数高达THREAD_PRIORITY_TIME_CRITICAL
。 确保有安全的代码,因为当调用线程忙于这些重点可锁定系统。 (Process.PriorityClass和Thread.Priority不太能够筹集到所需级别的优先级。)
可能增加系统借助于中断周期和时间的更新间隔多媒体计时器 。 (有各种.NET项目来封装这些多媒体定时器功能。)
在多核系统的核心的选择也会影响精度。 这是advantagous强行线程等待计时器事件上等待Processor0。 该SetThreadAffinityMask功能允许一个线程绑定到特定的CPU。 (.NET应用程序见Thread.ProcessorAffinity 。)
使用QueryPerformanceCounter的和QueryPerformanceFrequency的作为rescources高频时间的措施。 (.NET应用程序,请参阅: 创建QueryPerfCounter包装类 )
确保性能计数器的频率值进行校准。 返回的值QueryPerformanceFrequency
从由所观察到的值的偏移量,并通过一些热漂移偏差。 这可能/将介绍我们很多人/秒的误差。 请参阅的Windows时间戳项目 ,以了解如何这样的校准可以做到的。
和:是的,你可能要为一些硬编码。 但是微秒时间可以观察到非常可靠的Windows平台上。
注意:Windows不是一个实时操作系统。 但是,在Windows上使用的定时器是非常精确的。 他们做他们应该做什么和在非常高的精度做到这一点。 有很多有关Windows定时器和其准确性投诉的事实是,他们的行为依赖于底层硬件上不少。 这也是为什么该文件有许多缺陷的原因。 强烈建议诊断硬件,以便找到个人计时服务能力。 不幸的是这会导致任何程序有一些代码行额外独立于平台。
首先,你不知道时间是怎么远到当前第二,当你button2_click被调用。 因此,它基本上是随机当显示MessageBox的多少秒的离开 - 这是你所看到的。
其次,一个循环的时间跨度内有多少个CPU周期变得无关准确。
多少次任意给定的线程得到取决于什么其他的系统是怎么回事。 如果系统决定周期的一大堆需要去到另一个过程中,你的线程会被“饿死”了一段时间。
也许你可以详细说一下你真正想要做的,有人可以提供一些建议。
埃德的答案是正确的:在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
在系统上,检查Frequency
和IsHighResolution
属性)。 而我采样,以尽量减少其他进程抢占我的过程中,增加了进程的优先级。
但即便如此,因为Windows是不是一个RTOS,你能做的最好的是需要大量的样本,并使用一些统计数据来得到答案的近似值。