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