背景
我们知道在.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其它相关文章!