C# 利用Kernel32的QueryPerformanceCounter封装的 高精度定时器Timer

在日常的开发过程中,经常 会使用一些定时器做定时循环处理任务;

C# 中的定时器 包括 WinForm 中的System.Windows.Forms.Timer ;System.Timers.Timer ;System.Threading.Timer;

以上方法在实际运行中,在执行一段时间后都会或多或少出现时间精度丢失的情况。

经过测试:System.Windows.Forms.Timer  每各1秒钟要丢失15毫秒左右;

System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += timer_Elapsed;
timer.Interval = 1000;
timer.AutoReset = true;
timer.Enabled = true;

System.Threading.Timer 每各1秒钟要丢失12~24毫秒左右;

 System.Threading.Timer timer2;
 TimerCallback timerDelegate = new TimerCallback(timer2_Elapsed);
 //创建一个时间间隔为1s的定时器
 timer2 = new System.Threading.Timer(timerDelegate, null, 1000, 1000);

这样子的情况就会导致,原计划每分钟运行60次的,可能运行1分钟后,就会少于60次的情况;

因此在网上找了一个利用系统的Kernel32.dll 的QueryPerformanceCounter提高定时精度的手动封装的计时器;

测试的结果如下,基本保证在毫秒级;

调用方法和System.Windows.Forms.Timer的方法差不多,具体如下:

 // 方法3:启动HighAccurateTimer 定时器
 HighAccurateTimer timer3 = new HighAccurateTimer();
 timer3.Interval = 1000;
 timer3.Elapsed += new HighTimerEventHandler(timer3_Elapsed);
 timer3.Enabled = true;

// 停止定时器
timer3.Enabled = false;
timer3.Destroy();

HighAccurateTimerHelper 封装的代码如下:


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
 
namespace HighAccurateTimerHelper
{
    /// <summary>
    /// 定时器委托事件的参数
    /// </summary>
    public class TimerEventArgs : EventArgs
    {
        private long clockFrequency;
        public long ClockFrequency
        {
            get { return clockFrequency; }
        }
        private long previousTickCount;
        public long PreviousTickOunt
        {
            get { return previousTickCount; }
        }

        private long currentTickCount;
        public long CurrentTickCount
        {
            get { return currentTickCount; }
        }

        public TimerEventArgs(long clockFreq, long prevTick, long currTick)
        {
            this.clockFrequency = clockFreq;
            this.previousTickCount = prevTick;
            this.currentTickCount = currTick;
        }
    }
    /// <summary>
    /// 高精度定时器事件委托
    /// </summary>
    public delegate void HighTimerEventHandler(object sender, TimerEventArgs e);

    /// <summary>
    ///  高精度定时器,网上找的方法,利用Kernel32的QueryPerformanceCounter提高定时精度
    /// </summary>
    public class HighAccurateTimer
    {
        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(out long lpFrequency);


        public event HighTimerEventHandler Elapsed;

        Thread thread;
        private object threadLock = new object();

        private long clockFrequency = 0;
        private long intevalTicks = 0;
        private long nextTriggerTime = 0;

        private int intervalMs;
        /// <summary>
        /// 定时器间隔
        /// </summary>
        public int Interval
        {
            get
            {
                return intervalMs;
            }
            set
            {
                intervalMs = value;

            }
        }

        private bool enable;
        /// <summary>
        /// 启动定时器标志
        /// </summary>
        public bool Enabled
        {
            get
            {
                return enable;
            }
            set
            {
                enable = value;
                if (value == true)
                {
                    intevalTicks = (long)(((double)intervalMs / (double)1000) * (double)clockFrequency);
                    long currTick = 0;
                    GetTick(out currTick);
                    nextTriggerTime = currTick + intevalTicks;
                }else
                {
                    //Destroy();
                }
            }
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        public HighAccurateTimer()
        {
            if (QueryPerformanceFrequency(out clockFrequency) == false)
            {
                return;
            }
            this.intervalMs = 1000;
            this.enable = false;

            thread = new Thread(new ThreadStart(ThreadProc));
            thread.Name = "HighAccuracyTimer";
            thread.Priority = ThreadPriority.Highest;
            thread.Start();

        }

        /// <summary>
        /// 进程主程序
        /// </summary>
        private void ThreadProc()
        {
            long currTime;
            GetTick(out currTime);
            nextTriggerTime = currTime + intevalTicks;
            while (true)
            {
                while (currTime < nextTriggerTime)
                {
                    GetTick(out currTime); //决定时钟的精度
                }
                nextTriggerTime = currTime + intevalTicks;

                if (Elapsed != null && enable == true)
                {
                    Elapsed(this, new TimerEventArgs(clockFrequency, currTime - intevalTicks, currTime));
                }
            }
        }
        /// <summary>
        /// 获得当前时钟计数
        /// </summary>
        /// <param name="currentTickCount">时钟计数</param>
        /// <returns>获得是否成功</returns>
        public bool GetTick(out long currentTickCount)
        {
            if (QueryPerformanceCounter(out currentTickCount) == false)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        /// <summary>
        /// 注销定时器
        /// </summary>
        public void Destroy()
        {
            enable = false;
            thread.Abort();
        }
    }

}


 

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要设计一个高精度的 1 秒定时器,可以使用 C# 中的 QueryPerformanceCounterQueryPerformanceFrequency 函数。具体实现过程如下: 1. 使用 QueryPerformanceFrequency 函数获取计算机硬件时钟的频率,单位为每秒的计数。 ```csharp [DllImport("kernel32.dll")] public static extern bool QueryPerformanceFrequency(out long lpFrequency); long frequency; QueryPerformanceFrequency(out frequency); ``` 2. 使用 QueryPerformanceCounter 函数获取当前时刻的计数值。 ```csharp [DllImport("kernel32.dll")] public static extern bool QueryPerformanceCounter(out long lpPerformanceCount); long startCount; QueryPerformanceCounter(out startCount); ``` 3. 在一个循环中,不断获取当前时刻的计数值,并与起始计数值相减,得到经过的计数值差。将计数值差除以频率,即可得到经过的时间,单位为秒。若经过的时间大于等于 1 秒,则表示已经过了 1 秒,可以执行相应的操作,并将起始计数值更新为当前计数值。 ```csharp long currentCount; double elapsedTime; do { QueryPerformanceCounter(out currentCount); elapsedTime = (double)(currentCount - startCount) / frequency; } while (elapsedTime < 1.0); // 执行相应的操作 startCount = currentCount; ``` 完整的代码如下: ```csharp [DllImport("kernel32.dll")] public static extern bool QueryPerformanceFrequency(out long lpFrequency); [DllImport("kernel32.dll")] public static extern bool QueryPerformanceCounter(out long lpPerformanceCount); long frequency; QueryPerformanceFrequency(out frequency); long startCount; QueryPerformanceCounter(out startCount); while (true) { long currentCount; double elapsedTime; do { QueryPerformanceCounter(out currentCount); elapsedTime = (double)(currentCount - startCount) / frequency; } while (elapsedTime < 1.0); // 执行相应的操作 startCount = currentCount; } ``` 需要注意的是,这种高精度定时器的精度会受到多种因素的影响,如操作系统调度、CPU 占用等。因此,如果需要更高的精度和可靠性,可以考虑使用专门的硬件定时器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值