WindowsTimer计时不准确的问题及解决方法
在项目中,需要每隔20ms发送一个RTP数据包。一开始使用的是System.Windows.Forms下的Timer类,但是发现明显延迟了。用StopWatch测了一下,发现它的触发间隔居然不是20ms,而是在31ms左右摇摆。换了System.Threading下的Timer和System.Timers下和Timer也不行,一样的问题。
为什么会这样呢?在网上发现了一段非常具有启发性的话,它解释了原因并给出了解决的办法:
目前,Windows软件一般使用Timer定时器进行定时。Timer定时器是由应用程序响应定时消息WM_TIMER实现定时。Timer定时器是IBM PC硬件和ROM BIOS构造的定时器的简单扩充。PC的ROM初始化8253定时器来产生硬件中断08H,而08H中断的频率为18.2Hz,即至少每隔54.925 ms中断一次。此外,这个定时消息的优先权太低,只有在除WM_PAINT外的所有消息被处理完后,才能得到处理。
多媒体定时器也是利用系统定时器工作的,但它的工作机理和普通定时器有所不同。首先,多媒体定时器可以按精度要求设置8253的T/C0通道的计数初值,使定时器不存在54.945ms的限制;其次,多媒体定时器不依赖于消息机制,而是用函数产生一个独立的线程,在一定的中断次数到达后,直接调用预先设置好的回调函数进行处理,不必等到应用程序的消息队列为空,从而切实保障了定时中断得到实时响应,使其定时精度可达1ms。
这段话中的多媒体定时器被放在了winmm.dll中。要使用这个定时器,可以通过调用timeSetEvent和timeKillEvent函数来实现。为了便于使用,参照网上的一些资料,对这两个方法进行了封装。现跟大家分享一下。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Runtime.InteropServices;using System.Threading;using System.ComponentModel;publicsealedclass MillisecondTimer : IComponent, IDisposable{
//***************************************************** 字 段 *******************************************************************privatestatic TimerCaps caps;
privateint interval;
privatebool isRunning;
privateint resolution;
private TimerCallback timerCallback;
privateint timerID;
//***************************************************** 属 性 *******************************************************************
///<summary>
///
///</summary>
publicint Interval
{
get
{
returnthis.interval;
}
set
{
if( ( value < caps.periodMin ) || ( value > caps.periodMax ) )
{
thrownew Exception( "超出计时范围!" );
}
this.interval = value;
}
}
///<summary>
///
///</summary>
publicbool IsRunning
{
get
{
returnthis.isRunning;
}
}
///<summary>
///
///</summary>
public ISite Site
{
set;
get;
}
//***************************************************** 事 件 *******************************************************************
publicevent EventHandler Disposed; // 这个事件实现了IComponet接口
publicevent EventHandler Tick;
//*************************************************** 构造函数和释构函数 ******************************************************************
static MillisecondTimer()
{
timeGetDevCaps( ref caps, Marshal.SizeOf( caps ) );
}
public MillisecondTimer()
{
this.interval = caps.periodMin; //
this.resolution = caps.periodMin; //
this.isRunning = false;
this.timerCallback = new TimerCallback( this.TimerEventCallback );
}
public MillisecondTimer( IContainer container )
: this()
{
container.Add( this );
}
~MillisecondTimer()
{
timeKillEvent( this.timerID );
}
//***************************************************** 方 法 *******************************************************************
///<summary>
///
///</summary>
publicvoid Start()
{
if( !this.isRunning )
{
this.timerID = timeSetEvent( this.interval, this.resolution, this.timerCallback, 0, 1 ); // 间隔性地运行
if( this.timerID == 0 )
{
thrownew Exception( "无法启动计时器" );
}
this.isRunning = true;
}
}
///<summary>
///
///</summary>
publicvoid Stop()
{
if( this.isRunning )
{
timeKillEvent( this.timerID );
this.isRunning = false;
}
}
///<summary>
/// 实现IDisposable接口
///</summary>
publicvoid Dispose()
{
timeKillEvent( this.timerID );
GC.SuppressFinalize( this );
EventHandler disposed = this.Disposed;
if( disposed != null )
{
disposed( this, EventArgs.Empty );
}
}
//*************************************************** 内部函数 ******************************************************************
[DllImport( "winmm.dll" )]
privatestaticexternint timeSetEvent( int delay, int resolution, TimerCallback callback, int user, int mode );
[DllImport( "winmm.dll" )]
privatestaticexternint timeKillEvent( int id );
[DllImport( "winmm.dll" )]
privatestaticexternint timeGetDevCaps( ref TimerCaps caps, int sizeOfTimerCaps );
// The timeGetDevCaps function queries the timer device to determine its resolution.
privatevoid TimerEventCallback( int id, int msg, int user, int param1, int param2 )
{
if( this.Tick != null )
{
this.Tick( this, null ); // 引发事件
}
}
//*************************************************** 内部类型 ******************************************************************
privatedelegatevoid TimerCallback( int id, int msg, int user, int param1, int param2 ); // timeSetEvent所对应的回调函数的签名
///<summary>
/// 定时器的分辨率(resolution)。单位是ms,毫秒?
///</summary>
[StructLayout( LayoutKind.Sequential )]
privatestruct TimerCaps
{
publicint periodMin;
publicint periodMax;
}
}
调用方法:
MillisecondTimer timer1;privatevoid button1_Click( object sender, EventArgs e ){
timer1
= new MillisecondTimer();timer1.Interval
= 1;timer1.Tick
+= new EventHandler( timer1_Tick );timer1.Start();
}
int i;void timer1_Tick( object sender, EventArgs e ){
Console.WriteLine( i
++ );}
privatevoid button2_Click( object sender, EventArgs e ){
timer1.Stop();
timer1.Dispose();
}
以上是 WindowsTimer计时不准确的问题及解决方法 的全部内容, 来源链接: utcz.com/z/517077.html