精确到1ms的Timer

在项目中,需要每隔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;

public sealed class MillisecondTimer : IComponent, IDisposable
{

    //*****************************************************  字 段  *******************************************************************
    private static TimerCaps caps;
    private int interval;
    private bool isRunning;
    private int resolution;
    private TimerCallback timerCallback;
    private int timerID; 

    //*****************************************************  属 性  *******************************************************************
    /// <summary>
    ///
    /// </summary>
    public int Interval
    {
        get
        {
            return this.interval;
        }
        set
        {
            if( ( value < caps.periodMin ) || ( value > caps.periodMax ) )
            {
                throw new Exception( "超出计时范围!" );
            }
            this.interval = value;
        }
    }

    /// <summary>
    ///
    /// </summary>
    public bool IsRunning
    {
        get
        {
            return this.isRunning;
        }
    }

    /// <summary>
    ///
    /// </summary>
    public ISite Site
    {
        set;
        get;
    }

    //*****************************************************  事 件  *******************************************************************

    public event EventHandler Disposed;  // 这个事件实现了IComponet接口
    public event 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>
    public void Start()
    {
        if( !this.isRunning )
        {
            this.timerID = timeSetEvent( this.interval, this.resolution, this.timerCallback, 0, 1 ); // 间隔性地运行

            if( this.timerID == 0 )
            {
                throw new Exception( "无法启动计时器" );
            }
            this.isRunning = true;
        }
    }

    /// <summary>
    ///
    /// </summary>
    public void Stop()
    {
        if( this.isRunning )
        {
            timeKillEvent( this.timerID );
            this.isRunning = false;
        }
    }

    /// <summary>
    /// 实现IDisposable接口
    /// </summary>
    public void Dispose()
    {
        timeKillEvent( this.timerID );
        GC.SuppressFinalize( this );
        EventHandler disposed = this.Disposed;
        if( disposed != null )
        {
            disposed( this, EventArgs.Empty );
        }
    }

    //***************************************************  内部函数  ******************************************************************
    [DllImport( "winmm.dll" )]
    private static extern int timeSetEvent( int delay, int resolution, TimerCallback callback, int user, int mode );

    [DllImport( "winmm.dll" )]
    private static extern int timeKillEvent( int id );

    [DllImport( "winmm.dll" )]
    private static extern int timeGetDevCaps( ref TimerCaps caps, int sizeOfTimerCaps );
    //  The timeGetDevCaps function queries the timer device to determine its resolution. 

    private void TimerEventCallback( int id, int msg, int user, int param1, int param2 )
    {
        if( this.Tick != null )
        {
            this.Tick( this, null );  // 引发事件
        }
    }

    //***************************************************  内部类型  ******************************************************************

    private delegate void TimerCallback( int id, int msg, int user, int param1, int param2 ); // timeSetEvent所对应的回调函数的签名

    /// <summary>
    /// 定时器的分辨率(resolution)。单位是ms,毫秒?
    /// </summary>
    [StructLayout( LayoutKind.Sequential )]
    private struct TimerCaps
    {
        public int periodMin;
        public int periodMax;
    }

}

调用方法:

        MillisecondTimer timer1;

        private void 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++ );
        }

        private void button2_Click( object sender, EventArgs e )
        {
            timer1.Stop();
            timer1.Dispose();
        }

参考:

Microsecond and Millisecond C# Timer

.NET中的三种Timer的区别和用法(转)

时间: 2024-10-23 02:18:29

精确到1ms的Timer的相关文章

C#中自定义高精度Timer定时器的实例教程

Timer 用于以用户定义的事件间隔触发事件.Windows 计时器是为单线程环境设计的,其中,UI 线程用于执行处理.它要求用户代码有一个可用的 UI 消息泵,而且总是在同一个线程中操作,或者将调用封送到另一个线程. 使用此计时器时,请使用控件的Tick事件执行轮询操作,或在指定的时间内显示启动画面.每当 Enabled 属性设置为true且Interval属性大于0时,将引发Tick事件,引发的时间间隔基于Interval属性设置.System.Windows.Forms.Timer是应用于

任务调度之Timer与TimerTask配合

什么是任务调度? 在实际业务中,我们经常需要定时.定期.或者多次完成某些任务,对这些任务进行管理,就是任务调度.任务调度与多线程密切相关. 任务调度有多种方式 Timer与TimerTask配合 Timer 定时器,能在主线程外另起线程完成任务. TimerTask 实现Runnable接口的抽象类,相当于一个任务. 来一个简单的例子: import java.util.Timer; import java.util.TimerTask; public class TestTimer { pub

高精度定时器实现 z

1背景Permalink .NET Framework 提供了四种定时器,然而其精度都不高(一般情况下 15ms 左右),难以满足一些场景下的需求. 在进行媒体播放.绘制动画.性能分析以及和硬件交互时,可能需要 10ms 以下精度的定时器.这里不讨论这种需求是否合理,它是确实存在的问题,也有相当多的地方在讨论,说明这是一个切实的需求.然而,实现它并不是一件轻松的事情. 这里并不涉及内核驱动层面的定时器,只分析在 .NET 托管环境下应用层面的高精度定时器实现. Windows 不是实时操作系统,

使用timeSetEvent应注意事项

1.timeSetEvent最长时间间隔不能超过1000秒,即1000000毫秒,超过返回失败,可用CreateTimerQueryTimer或SetTimer(回调的方式)代替 2.timeSetEvent会生成一个独立的Timer回调线程,属多线程 3.timeSetEvent可创建高精度定时器,精确到1ms,SetTimer无法精确到1ms 4.timeKillEvent关掉定时器的函数,一定要一一对应,每次timeSetEvent返回的定时器的ID是不一样的,调用一次timeSetEve

双人黑白块

转载请标明地址:http://www.cnblogs.com/wangmengmeng/ 效果图: 源代码: 1 #undef UNICODE 2 #undef _UNICODE 3 #include <graphics.h> 4 #include <conio.h> 5 #include <stdio.h> 6 #include <time.h> 7 8 #define MAXTASK 50 // 定义游戏需要完成的黑块数量 9 10 // 定义宏 __s

单片机与控制实验(1)——数码管显示

一.实验目的和要求 初步学习和掌握MCS-51的体系结构和汇编语言,了解Keil编程环境和程序下载工具的使用方法.了解数码管输出的原理及编程方式. 二.实验设备 单片机测控实验系统 STC-ISP程序下载工具 Keil开发环境 三.实验内容 使用MCS-51汇编语言编写程序,完成如下功能: 1. 使用三个数码管显示十进制数值(001~999,可任意设置): 2. 每隔1秒,该数值自动减一,直到归零; 3. 归零后的下一秒,显示一个新的十进制数值(001~999,可任意设置): 4. 每隔1秒,新

高性能定时器(转自codeproject)

看了这篇文章, http://www.codeproject.com/Articles/2635/High-Performance-Timer-in-C 很不错的方法,虽然平时很少用到这么精确的定时器,但是这里我产生了一个想法,可以使用这个定时器, 用途: 1. 验证算法(code)执行的效率(时间上) 2.用于某些控制场合,比如需要对精确的时间周期 high-performance timer  class: /// <summary> /// A C# class to provide e

Linux 实时性能测试工具——Cyclictest 的使用与分析

关于Cyclictest工具,在Wiki上有说明:https://wiki.linuxfoundation.org/realtime/documentation/howto/tools/cyclictest Cyclictest is a high resolution test program, written by User:Tglx, maintained by Clark Williams and John Kacur Documentation Installation Get the

mq使用场景、不丢不重、时序性

mq使用场景.不丢不重.时序性.削峰 参考: http://zhuanlan.51cto.com/art/201704/536407.htm http://zhuanlan.51cto.com/art/201703/535090.htm http://zhuanlan.51cto.com/art/201704/536306.htm http://zhuanlan.51cto.com/art/201611/521602.htm http://zhuanlan.51cto.com/art/20161