龙空技术网

你一看就会的高精度定时器核心原理

小乖兽技术 116

前言:

而今咱们对“定时器基本原理”大概比较珍视,看官们都需要了解一些“定时器基本原理”的相关文章。那么小编在网上网罗了一些对于“定时器基本原理””的相关文章,希望姐妹们能喜欢,各位老铁们快快来学习一下吧!

我最近一直在捣鼓定时器这东西,大家都非常熟悉.Net框架中的这俩家伙,一个是System.Timers.Timer,另一个是System.Windows.Forms.Timer,这俩家伙用起来都是简简单单的,但是性能嘛,就是差点意思,达不到我们的要求。他们的精度受限于系统时钟,一般是15-16毫秒。找了很多资料,都找不到合适的、简洁的高精度定时器类。

偶然之间,我了解到一个叫做winmm.dll的类库。这个类库提供了许多API函数。我查阅了相关的API,结果发现有俩函数叫timeSetEvent和timeKillEvent,这两个函数可以设置和取消计时器事件。这可真是开启了不可思议的操作啊!

现在为你介绍这个家伙怎么搞:

认识winmm.dll库

winmm.dll库是 Windows 操作系统中的一个动态链接库,它主要用于提供多媒体相关的功能支持。winmm.dll 提供了许多 API 函数,这些函数可以用于实现音频、视频处理功能。不过我们这里先不用管这些API。我们重点关注timeSetEvent、timeKillEvent:设置和取消计时器事件。

timeSetEvent 函数

timeSetEvent 函数可以用来创建一个计时器事件,允许程序周期性地接收定时器消息。它的原型如下:

MMRESULT timeSetEvent(UINT uDelay,UINT uResolution,LPTIMECALLBACK lpTimeProc,DWORD_PTR dwUser,UINT fuEvent);`

其中各个参数的含义如下:

uDelay:指定定时器事件的延迟时间,单位为毫秒。uResolution:指定定时器的定时精度,一般为0。lpTimeProc:指向定时器消息处理函数的指针。dwUser:用户定义的数据,会传递给定时器消息处理函数。fuEvent:指定定时器事件的类型,可以是 TIME_ONESHOT 表示一次性事件,也可以是 TIME_PERIODIC 表示周期性事件。

timeSetEvent 函数成功调用后会返回一个标识符,用于标识创建的计时器事件。

timeKillEvent函数

timeKillEvent 函数用于取消由 timeSetEvent 创建的计时器事件,其原型如下:

MMRESULT timeKillEvent(UINT uTimerID);`

其中 uTimerID 是由 timeSetEvent 返回的计时器事件标识符。

这两个函数通常用于实现定时器功能,可以在特定的时间间隔内触发事件或执行特定的操作。可以利用这些函数来实现定时的任务调度、动画效果、定时器监控等功能,是多媒体应用程序中常用的功能之一。

定时器库函数封装

这个类库实现了一个高精度定时器 Timer,提供了定时器的启动、停止和触发事件等功能。使用了多媒体定时器API来实现定时器的功能。其中,timeGetDevCaps函数用于获取系统定时器的能力信息,timeSetEvent函数用于创建定时事件,timeKillEvent函数用于终止定时事件。

Timer类中包含了一些重要的属性和方法。其中,Period属性表示定时器的周期,Resolution属性表示定时器事件触发的最小间隔时间,Mode属性表示定时器的触发模式,IsRunning属性表示定时器是否正在运行。Start方法用于启动定时器,Stop方法用于停止定时器,Dispose方法用于释放资源。同时,还定义了Started、Stopped和Tick三个事件,分别表示定时器启动、停止和触发事件。

另外,该类还支持设置定时器事件处理程序所在的线程,通过SynchronizingObject属性来指定。

总的来说,提供了一个方便易用的高精度定时器,可以用于需要精确计时的场景,如音视频播放、游戏开发等。 类库是完整的代码,可以自己拿去进行测试。

核心逻辑

using System;using System.ComponentModel;using System.Diagnostics;using System.Runtime.InteropServices;namespace PrecisionTimer{    /// <summary>    /// 模式    /// </summary>    public enum Mode    {        /// <summary>        /// 单次触发模式        /// </summary>        OneShot,         /// <summary>        /// 周期触发模式        /// </summary>        Periodic    };    [StructLayout(LayoutKind.Sequential)]    public struct TimerCaps    {        /// <summary>        /// 最小周期        /// </summary>        public int periodMin;        /// <summary>        /// 最大周期        /// </summary>        public int periodMax;    }    public class TimerException : ApplicationException    {        public TimerException(string message) : base(message)        {        }    }    /// <summary>    /// 高精度定时器 Timer    /// </summary>    public sealed class Timer : IComponent    {        #region delegate        private delegate void TimeProc(int id, int msg, int user, int param1, int param2);        private delegate void EventRaiser(EventArgs e);        #endregion        #region 多媒体定时器API        /// <summary>        /// 多媒体定时器API:用于获取系统定时器的能力信息        /// </summary>        /// <param name="caps"></param>        /// <param name="sizeOfTimerCaps"></param>        /// <returns></returns>        [DllImport("winmm.dll")]        private static extern int timeGetDevCaps(ref TimerCaps caps,int sizeOfTimerCaps);        /// <summary>        /// 多媒体定时器API:创建一个定时事件        /// </summary>        /// <param name="delay"></param>        /// <param name="resolution"></param>        /// <param name="proc"></param>        /// <param name="user"></param>        /// <param name="mode"></param>        /// <returns></returns>        [DllImport("winmm.dll")]        private static extern int timeSetEvent(int delay, int resolution,TimeProc proc,int user,int mode);        /// <summary>        /// 多媒体定时器API:终止一个定时事件        /// </summary>        /// <param name="id"></param>        /// <returns></returns>        [DllImport("winmm.dll")]        private static extern int timeKillEvent(int id);        #endregion        #region Private Properties        private const int TIMERR_NOERROR = 0;        private int timerID;        private volatile Mode mode;        private volatile int period;        private volatile int resolution;        private TimeProc timeProcPeriodic;        private TimeProc timeProcOneShot;        private EventRaiser tickRaiser;        private bool running = false;        private volatile bool disposed = false;        private ISynchronizeInvoke synchronizingObject = null;        private ISite site = null;        private static TimerCaps caps;        #endregion        #region Public Events        public event EventHandler Started;        public event EventHandler Stopped;        public event EventHandler Tick;        #endregion        #region Ctor        static Timer()        {            // Get multimedia timer capabilities.            timeGetDevCaps(ref caps, Marshal.SizeOf(caps));        }        public Timer(IContainer container)        {            container.Add(this);            Initialize();        }        public Timer()        {            Initialize();        }        ~Timer()        {            if (IsRunning)            {                // Stop and destroy timer.                timeKillEvent(timerID);            }        }        private void Initialize()        {            this.mode = Mode.Periodic;            this.period = Capabilities.periodMin;            this.resolution = 1;            running = false;            timeProcPeriodic = new TimeProc(TimerPeriodicEventCallback);            timeProcOneShot  = new TimeProc(TimerOneShotEventCallback);            tickRaiser       = new EventRaiser(OnTick);        }        #endregion        #region 触发事件        public void Start()        {            if (disposed)            {                throw new ObjectDisposedException("Timer");            }            if (IsRunning)            {                return;            }            if (Mode == Mode.Periodic)            {                timerID = timeSetEvent(Period, Resolution, timeProcPeriodic, 0, (int)Mode);            }            else            {                timerID = timeSetEvent(Period, Resolution, timeProcOneShot, 0, (int)Mode);            }            if (timerID != 0)            {                running = true;                if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)                {                    SynchronizingObject.BeginInvoke(                        new EventRaiser(OnStarted),                        new object[] { EventArgs.Empty });                }                else                {                    OnStarted(EventArgs.Empty);                }            }            else            {                throw new TimerException("Unable to start timer.");            }        }        public void Stop()        {            if (disposed)            {                throw new ObjectDisposedException("Timer");            }            if (!running)            {                return;            }            int result = timeKillEvent(timerID);            Debug.Assert(result == TIMERR_NOERROR);            running = false;            if (SynchronizingObject != null && SynchronizingObject.InvokeRequired)            {                SynchronizingObject.BeginInvoke(                    new EventRaiser(OnStopped),                    new object[] { EventArgs.Empty });            }            else            {                OnStopped(EventArgs.Empty);            }        }        public void Dispose()        {            if (disposed)            {                return;            }            if (IsRunning)            {                Stop();            }            disposed = true;            OnDisposed(EventArgs.Empty);        }        private void TimerPeriodicEventCallback(int id, int msg, int user, int param1, int param2)        {            if (synchronizingObject != null)            {                synchronizingObject.BeginInvoke(tickRaiser, new object[] { EventArgs.Empty });            }            else            {                OnTick(EventArgs.Empty);            }        }        private void TimerOneShotEventCallback(int id, int msg, int user, int param1, int param2)        {            if (synchronizingObject != null)            {                synchronizingObject.BeginInvoke(tickRaiser, new object[] { EventArgs.Empty });                Stop();            }            else            {                OnTick(EventArgs.Empty);                Stop();            }        }        // 触发 Disposed 事件.        private void OnDisposed(EventArgs e)        {            EventHandler handler = Disposed;            if (handler != null)            {                handler(this, e);            }        }        // 触发 Started 事件.        private void OnStarted(EventArgs e)        {            EventHandler handler = Started;            if (handler != null)            {                handler(this, e);            }        }        // 触发 Stopped 事件.        private void OnStopped(EventArgs e)        {            EventHandler handler = Stopped;            if (handler != null)            {                handler(this, e);            }        }        // 触发 Tick 事件.        private void OnTick(EventArgs e)        {            EventHandler handler = Tick;            if (handler != null)            {                handler(this, e);            }        }        /// <summary>        /// 获取或设置用于调度事件处理程序调用的对象。        /// </summary>        public ISynchronizeInvoke SynchronizingObject        {            get            {                #region Require                if (disposed)                {                    throw new ObjectDisposedException("Timer");                }                #endregion                return synchronizingObject;            }            set            {                #region Require                if (disposed)                {                    throw new ObjectDisposedException("Timer");                }                #endregion                synchronizingObject = value;            }        }        #endregion        #region Public Properties        /// <summary>        /// 周期        /// </summary>        public int Period        {            get            {                #region Require                if (disposed)                {                    throw new ObjectDisposedException("Timer");                }                #endregion                return period;            }            set            {                #region Require                if (disposed)                {                    throw new ObjectDisposedException("Timer");                }                else if (value < Capabilities.periodMin || value > Capabilities.periodMax)                {                    throw new ArgumentOutOfRangeException("Period", value,                        "Multimedia Timer period out of range.");                }                #endregion                period = value;                if (IsRunning)                {                    Stop();                    Start();                }            }        }        /// <summary>        /// 定时器事件触发的最小间隔时间        /// </summary>        public int Resolution        {            get            {                if (disposed)                {                    throw new ObjectDisposedException("Timer");                }                return resolution;            }            set            {                if (disposed)                {                    throw new ObjectDisposedException("Timer");                }                else if (value < 0)                {                    throw new ArgumentOutOfRangeException("Resolution", value,"timer resolution out of range.");                }                resolution = value;                if (IsRunning)                {                    Stop();                    Start();                }            }        }        /// <summary>        /// 触发模式        /// </summary>        public Mode Mode        {            get            {                if (disposed)                {                    throw new ObjectDisposedException("Timer");                }                return mode;            }            set            {                if (disposed)                {                    throw new ObjectDisposedException("Timer");                }                mode = value;                if (IsRunning)                {                    Stop();                    Start();                }            }        }        /// <summary>        /// 是否正则运行        /// </summary>        public bool IsRunning        {            get            {                return running;            }        }        /// <summary>        /// 获取定时器的能力信息        /// </summary>        public static TimerCaps Capabilities        {            get            {                return caps;            }        }        #endregion                #region IComponent        public event System.EventHandler Disposed;        public ISite Site        {            get            {                return site;            }            set            {                site = value;            }        }        #endregion    }}
测试代码
public partial class MainWindow : Window{    private PrecisionTimer.Timer mTimer = null;    private DateTime lastTimerTick = DateTime.Now;    private int index = 0;    public MainWindow()    {        InitializeComponent();    }    private void btnLoad_Click(object sender, RoutedEventArgs e)    {        mTimer = new PrecisionTimer.Timer();        mTimer.Period = 20;        mTimer.Tick += new EventHandler(OnTimerTick);        lastTimerTick = DateTime.Now;        mTimer.Start();    }    private void OnTimerTick(object sender, EventArgs e)    {        index++;        lastTimerTick = DateTime.Now;        Trace.WriteLine($"{lastTimerTick.ToString("HH:mm:ss ffff")} ThreadId:{Thread.CurrentThread.ManagedThreadId.ToString()},index = {index}");    }    protected override void OnClosed(EventArgs e)    {        base.OnClosed(e);        mTimer.Stop();        mTimer.Dispose();    }  }}
输出结果
19:20:20 3015 ThreadId:7,index = 119:20:20 3167 ThreadId:7,index = 219:20:20 3368 ThreadId:7,index = 319:20:20 3569 ThreadId:7,index = 419:20:20 3770 ThreadId:7,index = 519:20:20 3970 ThreadId:7,index = 619:20:20 4170 ThreadId:7,index = 719:20:20 4367 ThreadId:7,index = 819:20:20 4567 ThreadId:7,index = 919:20:20 4767 ThreadId:7,index = 10
和原生Timer对比

与Windows Forms自带的Timer控件相比,该Timer类有一些区别和优势:

精度更高:PrecisionTimer使用了Windows多媒体定时器(Multimedia Timer),可以提供更高的计时精度,最小精度为1毫秒。而Windows Forms自带的Timer控件的精度受系统时钟的限制,一般为15-16毫秒。可调节的周期和分辨率:PrecisionTimer允许用户设置定时器的周期和分辨率,可以根据具体需求进行调整。而Windows Forms自带的Timer控件的周期固定为1000毫秒(1秒),无法更改。支持多种模式:PrecisionTimer支持两种模式:OneShot(单次触发)和Periodic(周期性触发),可以根据需要选择不同的模式。而Windows Forms自带的Timer控件仅支持周期性触发。可以选择事件处理线程:PrecisionTimer可以通过SynchronizingObject属性指定事件处理的线程,从而避免在多线程环境下引发线程冲突的问题。而Windows Forms自带的Timer控件的Tick事件总是在UI线程上触发。更灵活的事件处理:PrecisionTimer定义了Started、Stopped、Tick和Disposed事件,可以方便地对定时器的状态变化进行监听和处理。而Windows Forms自带的Timer控件仅提供Tick事件。

如果你有兴趣,可以试试,欢迎评论留言一起交流。

#挑战30天在头条写日记#

#自律学习计划#

#实话实说#

#定时器#

标签: #定时器基本原理 #定时器基本原理图解视频