美文网首页
C#高精度定时器

C#高精度定时器

作者: 安静的程序员 | 来源:发表于2020-05-26 12:51 被阅读0次

因在项目中需要使用定时器播放帧动画,系统自带的定时器每秒最多执行60帧,因为误差较大,如果用于计时的任务,则会随着时间的增加,误差会变的越来越大。
好在Windows系统提供了一种精度更高的定时器:多媒体定时器(Multimedia Timers),最高可以精确至1毫秒。
微软官方文档:https://docs.microsoft.com/zh-cn/windows/win32/multimedia/multimedia-timers
为了方便使用,我封装了一个库,下面是主要代码(完整代码请参考附件):

namespace HighPrecisionTimer
{
    /// <summary>
    /// 定时器分辨率:毫秒
    /// </summary>
    struct TimerCaps
    {
        /// <summary>最小周期</summary>
        public int periodMin;
        /// <summary>最大周期</summary>
        public int periodMax;
    }

    /// <summary>
    /// 高精度定时器
    /// </summary>
    public class HPTimer
    {
        static HPTimer()
        {
            TimeGetDevCaps(ref _caps, Marshal.SizeOf(_caps));
        }

        public HPTimer()
        {
            Running = false;
            _interval = _caps.periodMin;
            _resolution = _caps.periodMin;
            _callback = new TimerCallback(TimerEventCallback);
        }

        ~HPTimer()
        {
            TimeKillEvent(_id);
        }

        /// <summary>
        /// 系统定时器回调
        /// </summary>
        /// <param name="id">定时器编号</param>
        /// <param name="msg">预留,不使用</param>
        /// <param name="user">用户实例数据</param>
        /// <param name="param1">预留,不使用</param>
        /// <param name="param2">预留,不使用</param>
        private delegate void TimerCallback(int id, int msg, int user, int param1, int param2);

        #region 动态库接口

        /// <summary>
        /// 查询设备支持的定时器分辨率
        /// </summary>
        /// <param name="ptc">定时器分辨率结构体指针</param>
        /// <param name="cbtc">定时器分辨率结构体大小</param>
        /// <returns></returns>
        [DllImport("winmm.dll", EntryPoint = "timeGetDevCaps")]
        private static extern TimerError TimeGetDevCaps(ref TimerCaps ptc, int cbtc);

        /// <summary>
        /// 绑定定时器事件
        /// </summary>
        /// <param name="delay">延时:毫秒</param>
        /// <param name="resolution">分辨率</param>
        /// <param name="callback">回调接口</param>
        /// <param name="user">用户提供的回调数据</param>
        /// <param name="eventType"></param>
        [DllImport("winmm.dll", EntryPoint = "timeSetEvent")]
        private static extern int TimeSetEvent(int delay, int resolution, TimerCallback callback, int user, int eventType);

        /// <summary>
        /// 终止定时器
        /// </summary>
        /// <param name="id">定时器编号</param>
        [DllImport("winmm.dll", EntryPoint = "timeKillEvent")]
        private static extern TimerError TimeKillEvent(int id);

        #endregion

        #region 属性

        /// <summary>时间间隔:毫秒</summary>
        public int Interval
        {
            get { return _interval; }
            set
            {
                if (value < _caps.periodMin || value > _caps.periodMax)
                    throw new Exception("无效的计时间隔");
                _interval = value;
            }
        }

        public bool Running { get; private set; }

        #endregion

        #region 事件

        public event Action Ticked;

        #endregion

        #region 公开方法

        public void Start()
        {
            if (!Running)
            {
                _id = TimeSetEvent(_interval, _resolution, _callback, 0,
                    (int)EventType01.TIME_PERIODIC | (int)EventType02.TIME_KILL_SYNCHRONOUS);
                if (_id == 0) throw new Exception("启动定时器失败");
                Running = true;
            }
        }

        public void Stop()
        {
            if (Running)
            {
                TimeKillEvent(_id);
                Running = false;
            }
        }

        #endregion

        #region 内部方法

        private void TimerEventCallback(int id, int msg, int user, int param1, int param2)
        {
            Ticked?.Invoke();
        }

        #endregion

        #region 字段

        // 系统定时器分辨率
        private static TimerCaps _caps;
        // 定时器间隔
        private int _interval;
        // 定时器分辨率
        private int _resolution;
        // 定时器回调
        private TimerCallback _callback;
        // 定时器编号
        private int _id;

        #endregion
    }
}

完整项目:https://pan.baidu.com/s/1Lz6PbGsj4If0Y8jbdaQigg
提取码:jkl2

相关文章

  • C#高精度定时器

    因在项目中需要使用定时器播放帧动画,系统自带的定时器每秒最多执行60帧,因为误差较大,如果用于计时的任务,则会随着...

  • Linux 设备驱动之内核定时器 2020-02-20

    该内核定时器的实现是基于低精度定时器实现,高精度定时器的实现代码更为复杂,将在其他章节做相应介绍struct ti...

  • 高精度(加法&乘法&减法)

    高精度加法: 高精度乘法: 高精度减法:

  • GCD定时器

    GCD定时器会自己开启一条子线程,子线程也会自己开启runloop,自己创建管理,所以需要高精度计时时,用GCD计...

  • PHP算术及精度计算

    一、高精度算术运算符 bcadd 将两个高精度数字相加bccomp 比较两个高精度数字,返...

  • 几个高精度模板

    模板来自洛谷及Acwing:Acwing洛谷 后续增加注释以及相关代码改进 高精度加法 高精度减法 高精度乘法 高...

  • 更可靠和高精度的 iOS 定时器

    定时器一般用于延迟一段时间执行特定的代码,必要的话按照指定的频率重复执行。iOS 中延时执行有多种方式,常用的有:...

  • php高精度计算

    bcadd — 将两个高精度数字相加 bcdiv — 将两个高精度数字相除 bcmod — 求高精度数字余数 bc...

  • sicily_1029 Rabbit

    标签: sicily 高精度计算 题目链接: http://soj.sysu.edu.cn/1029 思路 高精度...

  • Lesson 2:高精度地图

    从 Apollo 起步-Lesson 2:高精度地图 高精度地图 导航地图(Navigation Map)VS高精...

网友评论

      本文标题:C#高精度定时器

      本文链接:https://www.haomeiwen.com/subject/hjbmahtx.html