背景

 我们知道在.net framework中存在四种常用的定时器,他们分别是:

1 两个是通用的多线程定时器:

  • system.threading.timer
  • system.timers.timer

2 两个是专用的单线程定时器

  • system.windows.forms.timer (windows forms 的定时器)
  • system.windows.threading.dispatchertimer (wpf 的定时器)

通常他们的精度只能维持在10-20ms之间,这个和操作系统相关,所以我们在很多场景下面这个是不能够达到我们精度的要求的,如果要实现这一需求我们该怎么办,当然也有很多办法,今天主要介绍一种stopwatch来实现的方式,网上有很多采用win32 dll的api这个当然是可以的,这篇文章的重点不是去讨论这个,关于使用win32 api的方式可以参考这里。

实现

using system;
using system.collections.generic;
using system.diagnostics;
using system.linq;
using system.runtime.interopservices;
using system.text;
 
namespace pangea.common.utility
{
    /// <summary>
    /// .net stopwatch对高精度定时器作了很好的包装
    /// devicetimer内部采用stopwatch类实现高精度定时操作
    /// </summary>
    public sealed class devicetimer
    {
#if use_cpu_counting
        //引入高性能计数器api,通过对cpu计数完成计时
        [dllimport("kernel32.dll")]
        private static extern bool queryperformancecounter(out long lpperformancecount);
 
        //获取当前cpu的工作频率
        [dllimport("kernel32.dll")]
        private static extern bool queryperformancefrequency(out long lpfrequency);
#else
        /// <summary>
        /// 获取tickcount64计数
        /// </summary>
        /// <returns></returns>
        //[dllimport("kernel32.dll")]
        //public static extern long gettickcount64();
#endif
        private enum devicetimerstate
        {
            tm_st_idle = 0,
            tm_st_busy = 1,
            tm_st_timeout = 2,
        }
 
        /// <summary>
        /// stopwatch object
        /// </summary>
        stopwatch _stopwatch = new stopwatch();
 
        /// <summary>
        /// 定时器内部状态
        /// </summary>
        devicetimerstate _state;
 
        /// <summary>
        /// 定时器开始计时时刻的相对时间点
        /// </summary>
        long _starttime;
 
        /// <summary>
        /// 定时器超时时刻的相对时间点
        /// </summary>
        long _timeout;
 
#if use_cpu_counting
 
        /// <summary>
        /// cpu运行的时钟频率
        /// </summary>
        double _freq;
#endif
 
        /// <summary>
        /// 定时时间(单位:ms)
        /// </summary>
        double _duration;
 
        /// <summary>
        /// class constructure
        /// </summary>
        public devicetimer()
        {
#if use_cpu_counting
            long freq;
            if (queryperformancefrequency(out freq) == false)
                throw new exception("本计算机不支持高性能计数器");
            //得到每1ms的cpu计时tickcount数目
            _freq = (double)freq / 1000.0;
            queryperformancecounter(out _starttime);
#else
            _stopwatch.start();
            _starttime = 0;
#endif
            setstate(devicetimerstate.tm_st_idle);
            _timeout = _starttime;
            _duration = 0;
        }
 
        /// <summary>
        /// 内部调用:设置定时器当前状态
        /// </summary>
        /// <param name="state"></param>
        private void setstate(devicetimerstate state)
        {
            _state = state;
        }
 
        /// <summary>
        /// 内部调用:返回定时器当前状态
        /// </summary>
        /// <returns></returns>
        private devicetimerstate getstate()
        {
            return _state;
        }
 
        /// <summary>
        /// 定时器开始计时到现在已流逝的时间(单位:毫秒)
        /// </summary>
        /// <returns></returns>
        public double getelapsetime()
        {
            long curcount;
#if use_cpu_counting
            queryperformancecounter(out curcount);
            return (double)(curcount - _starttime) / (double)_freq;
#else
            curcount = _stopwatch.elapsedmilliseconds;
            return curcount - _starttime;
#endif
        }
 
        /// <summary>
        /// 获取定时总时间
        /// </summary>
        /// <returns></returns>
        public double gettotaltime()
        {
            return _duration;
        }
 
        /// <summary>
        /// 停止计时器计时
        /// </summary>
        public void stop()
        {
            setstate(devicetimerstate.tm_st_idle);
#if use_cpu_counting
            queryperformancecounter(out _starttime);
#else
            _starttime = _stopwatch.elapsedmilliseconds;
#endif
            _timeout = _starttime;
            _duration = 0;
        }
 
        /// <summary>
        /// 启动定时器
        /// </summary>
        /// <param name="delay_ms">定时时间(单位:毫秒)</param>
        public void start(double delay_ms)
        {
#if use_cpu_counting
            queryperformancecounter(out _starttime);
            _timeout = convert.toint64(_starttime + delay_ms * _freq);
#else
            _starttime = _stopwatch.elapsedmilliseconds;
            _timeout = convert.toint64(_starttime + delay_ms);
#endif
            setstate(devicetimerstate.tm_st_busy);
            _duration = delay_ms;
        }
 
        /// <summary>
        /// 重新开始定时器
        /// 开始的计时时间以上一次start的时间为准
        /// </summary>
        /// <param name="delay_ms">定时时间(单位:毫秒)</param>
        public void restart(double delay_ms)
        {
#if use_cpu_counting
            _timeout = convert.toint64(_starttime + delay_ms * _freq);
#else
            _timeout = convert.toint64(_starttime + delay_ms);
#endif
            setstate(devicetimerstate.tm_st_busy);
            _duration = delay_ms;
        }
 
        /// <summary>
        /// 返回定时器是否超时
        /// </summary>
        /// <returns></returns>
        public bool istimeout()
        {
            if (_state == devicetimerstate.tm_st_idle)
            {
                //system.diagnostics.debug.writeline("warning: misuage of the device timer. you must start it first before you can use it.");
                //system.diagnostics.debug.assert(false, "warning: misuage of the device timer. you must start it first before you can use it.");
            }
            long curcount;
#if use_cpu_counting
            queryperformancecounter(out curcount);
#else
            curcount = _stopwatch.elapsedmilliseconds;
#endif
            if (_state == devicetimerstate.tm_st_busy && (curcount >= _timeout))
            {
                setstate(devicetimerstate.tm_st_timeout);
                return true;
            }
            else if (_state == devicetimerstate.tm_st_timeout)
            {
                return true;
            }
            return false;
        }
 
        /// <summary>
        /// 定时器是否在工作中
        /// </summary>
        /// <returns></returns>
        public bool isidle()
        {
            return (_state == devicetimerstate.tm_st_idle);
        }
    }
}

  这个里面我们在devicetimer中定义了一个私有的_stopwatch 对象并且在构造函数中就启动了这个stopwatch,所以我们在使用的时候是通过先创建一个deveicetimer的对象然后我们再调用内部的start方法,当然在调用这个方法的时候我们需要传入一个定时时间,然后不断检测istimeout方法看是否到达定时时间,从而达到类似于定时时间到的效果,另外getelapsetime()方法能够获取从调用start方法开始到现在的时间,另外我们还在其中定义了几个枚举值用来表示当前devicetimer的状态用于做一些状态的校验,具体数值如下。

private enum devicetimerstate
{
    tm_st_idle = 0,
    tm_st_busy = 1,
    tm_st_timeout = 2,
}

  这里还有最后一个问题就是循环调用的问题,这个其实也是非常简单就在一个while循环中不断进行调用,当然下面的代码可以有很多内容供我们去发挥的,这个可以根据自己的需要进行修改。

using system;
using system.threading.tasks;
 
namespace devicetimerconsoleapp
{
    class program
    {
        private static bool flag = true;
        static void main(string[] args)
        {
            task.factory.startnew(() =>
            {
                var devicetimer = new devicetimer();
                devicetimer.start(5);
 
                while (flag)
                {
                    if (devicetimer.istimeout())
                    {
                        console.writeline($"定时时间已到,距离开始执行已过去:{devicetimer.getelapsetime()}ms");
 
                        devicetimer.start(5);
                    }
                }
            });
            console.readkey();
        }
    }
     
}

  我们来看看定时器的效果

以上就是c# 创建高精度定时器的示例的详细内容,更多关于c# 创建高精度定时器的资料请关注www.887551.com其它相关文章!