项目里面用到的这些看起来名字高大上的定时器测试下来也是非常不准。看了源码发现也是用System.Timers.Timer或者用的是Thread休眠的方式来实现的。100毫秒就不准了。直到一番搜索,发现利用多媒体定时器winmm.dll的MillisecondTimer是可用的。原文来自博客(dehai)Timer计时不准确的问题及解决方法”。代码如下:

 public sealed class MillisecondTimer : IComponent, IDisposable
{
//***************************************************** 字 段 *******************************************************************
private static TimerCaps caps;
private int interval;
private bool isRunning;
private int resolution;
private TimerCallback timerCallback;
private int timerID; //***************************************************** 属 性 *******************************************************************
/// <summary>
///
/// </summary>
public int Interval
{
get
{
return this.interval;
}
set
{
if ((value < caps.periodMin) || (value > caps.periodMax))
{
throw new Exception("超出计时范围!");
}
this.interval = value;
}
} /// <summary>
///
/// </summary>
public bool IsRunning
{
get
{
return this.isRunning;
}
} /// <summary>
///
/// </summary>
public ISite Site
{
set;
get;
} //***************************************************** 事 件 *******************************************************************
public event EventHandler Disposed; // 这个事件实现了IComponet接口
public event EventHandler Tick;
//*************************************************** 构造函数和释构函数 ****************************************************************** static MillisecondTimer()
{
timeGetDevCaps(ref caps, Marshal.SizeOf(caps));
} public MillisecondTimer()
{
this.interval = caps.periodMin; //
this.resolution = caps.periodMin; //
this.isRunning = false;
this.timerCallback = new TimerCallback(this.TimerEventCallback);
} public MillisecondTimer(IContainer container)
: this()
{
container.Add(this);
} ~MillisecondTimer()
{
timeKillEvent(this.timerID);
} //***************************************************** 方 法 *******************************************************************
/// <summary>
///
/// </summary>
public void Start()
{
if (!isRunning)
{
timerID = timeSetEvent(this.interval, this.resolution, this.timerCallback, 0, 1); // 间隔性地运行
if (timerID == 0)
{
throw new Exception("无法启动计时器");
}
isRunning = true;
}
} /// <summary>
///
/// </summary>
public void Stop()
{
if (isRunning)
{
timeKillEvent(this.timerID);
isRunning = false;
}
} /// <summary>
/// 实现IDisposable接口
/// </summary>
public void Dispose()
{
timeKillEvent(this.timerID);
GC.SuppressFinalize(this);
Disposed?.Invoke(this, EventArgs.Empty);
} //*************************************************** 内部函数 ******************************************************************
[DllImport("winmm.dll")]
private static extern int timeSetEvent(int delay, int resolution, TimerCallback callback, int user, int mode); [DllImport("winmm.dll")]
private static extern int timeKillEvent(int id); [DllImport("winmm.dll")]
private static extern int timeGetDevCaps(ref TimerCaps caps, int sizeOfTimerCaps); private void TimerEventCallback(int id, int msg, int user, int param1, int param2)
{
Tick?.Invoke(this, null); // 引发事件
}
//*************************************************** 内部类型 ****************************************************************** private delegate void TimerCallback(int id, int msg, int user, int param1, int param2); // timeSetEvent所对应的回调函数的签名 /// <summary>
/// 定时器的分辨率(resolution)。单位是ms,毫秒?
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct TimerCaps
{
public int periodMin;
public int periodMax;
}
}
}

测试代码

 class Program
{
static Dictionary<string, int> dtList = new Dictionary<string, int>();
static void Main(string[] args)
{
//var timer = new DoubleThreadTimer(100, 100);
//timer.OnRunningCallback += Timer1_Tick;
//timer.Start(); var timer1 = new MillisecondTimer();
timer1.Interval = 2;
timer1.Tick += Timer1_Tick;
timer1.Start();
Console.ReadLine();
} private static void Timer1_Tick(object sender, EventArgs e)
{
var dt = DateTime.Now;
string dtStr = dt.ToString("yyyyMMdd HHmmss");
if (dtList.ContainsKey(dtStr))
{
dtList[dtStr]++;
if (dtList[dtStr] % 500 == 0)
{
Console.WriteLine(dtStr + "--->" + dtList[dtStr]);
dtList.Remove(dtStr);
}
}
else
{
if (dtList.Count > 1)
{
foreach (var item in dtList)
{
Console.WriteLine(item.Key + "--->" + item.Value);
}
dtList.Clear();
}
dtList.Add(dtStr, 1);
}
}
}

  

测试结论: 时间间隔设置为5毫秒,表现很稳定。2毫秒有时准。可靠性比.net的timer准好多倍啊

加上了些注释,并针对项目进行了封装:

/// <summary>
/// 用Windows多媒体定时器winmm.dll库封装的定时器
/// </summary>
public class MillisecondTimer : ITimer, IDisposable
{
#region 定时器事件类型
const int TIME_ONESHOT = 0;
const int TIME_PERIODIC = 1;
#endregion private bool isRunning = false;
/// <summary>
/// 定时器的分辨率(resolution)。单位是毫秒
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct TimerCaps
{
public int periodMin;
public int periodMax;
}
#region winmm.dll API声明
/// <summary>
/// 设置一个媒体定时器(定时器事件类型常用的有2种,0:TIME_ONESHOT,1:TIME_PERIODIC)
/// </summary>
/// <param name="delay">以毫秒指定事件的周期。</param>
/// <param name="resolution">以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。</param>
/// <param name="callback">指向一个回调函数。(委托实例)</param>
/// <param name="user">存放用户提供的回调数据。</param>
/// <param name="mode">指定定时器事件类型:0->TIME_ONESHOT:uDelay毫秒后只产生一次事件;1->TIME_PERIODIC :每隔delay毫秒周期性地产生事件。</param>
/// <returns>定时器的ID,释放资源的时候需要</returns>
[DllImport("winmm.dll")]
private static extern int timeSetEvent(int delay, int resolution, TimerCallback callback, int user, int mode);
/// <summary>
/// 结束定时器,释放资源
/// </summary>
/// <param name="id">设置定时器返回的ID</param>
/// <returns></returns>
[DllImport("winmm.dll")]
private static extern int timeKillEvent(int id);
/// <summary>
/// 初始化结构体,TimerCaps{periodMin,periodMax}
/// </summary>
/// <param name="caps">TimerCaps</param>
/// <param name="sizeOfTimerCaps">TimerCaps的长度</param>
/// <returns></returns>
[DllImport("winmm.dll")]
private static extern int timeGetDevCaps(ref TimerCaps caps, int sizeOfTimerCaps);
#endregion
private delegate void TimerCallback(int id, int msg, int user, int param1, int param2); // timeSetEvent所对应的回调函数的签名
public event EES.Common.ManualTimer.TimerCallback OnRunningCallback;
public event EES.Common.ManualTimer.TimerCallback OnStartedCallback;
public event EES.Common.ManualTimer.TimerCallback OnStopedCallback; private TimerCallback m_TimerCallback;
private int timerID;
private TimerCaps caps = new TimerCaps(); public MillisecondTimer(int dueTime, int period)
{
timeGetDevCaps(ref caps, Marshal.SizeOf(caps));
caps.periodMin = period;
caps.periodMax = dueTime;
isRunning = false;
m_TimerCallback = new TimerCallback(TimerEventCallback);
} /// <summary>
/// 触发事件
/// </summary>
/// <param name="id"></param>
/// <param name="msg"></param>
/// <param name="user"></param>
/// <param name="param1"></param>
/// <param name="param2"></param>
private void TimerEventCallback(int id, int msg, int user, int param1, int param2)
{
OnRunningCallback?.Invoke(this, new EventArgs());
} /// <summary>
/// 启动定时器,回调OnStartedCallback
/// </summary>
public void Start()
{
if (!isRunning)
{
timerID = timeSetEvent(caps.periodMin, caps.periodMin, m_TimerCallback, 0, TIME_PERIODIC); // 间隔性地运行
GC.KeepAlive(m_TimerCallback);
if (timerID == 0)
{
throw new EESException("无法启动计时器");
}
isRunning = true;
if (isRunning)
OnStartedCallback?.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
///停止定时器
/// </summary>
public void Stop()
{
if (isRunning)
{
timeKillEvent(timerID);
isRunning = false;
OnStopedCallback?.Invoke(this, EventArgs.Empty);
Dispose();
}
}
/// <summary>
/// 获取定时器允许状态
/// </summary>
/// <returns></returns>
public bool IsRunning()
{
return isRunning;
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
if (!timerID.Equals(0))
timeKillEvent(timerID);
GC.SuppressFinalize(this);
}
}

  

Windows高速定时器,多媒体定时器winmm.dll库的使用的更多相关文章

  1. CVE-2012-0003 Microsoft Windows Media Player ‘winmm.dll’ MIDI文件解析远程代码执行漏洞 分析

    [CNNVD]Microsoft Windows Media Player ‘winmm.dll’ MIDI文件解析远程代码执行漏洞(CNNVD-201201-110)    Microsoft Wi ...

  2. winmm.dll包含函数

    DLL 文件: winmm 或者 winmm.dll DLL 名称: Windows Multimedia API 描述: winmm.dll是Windows多媒体相关应用程序接口,用于低档的音频和游 ...

  3. VC++或QT下 高精度 多媒体定时器

    在VC编程中,用SetTimer可以定义一个定时器,到时间了,就响应OnTimer消息,但这种定时器精度太低了.如果需要精度更高一些的定时器(精 确到1ms),可以使用下面的高精度多媒体定时器进行代码 ...

  4. CVE-2012-0003:Microsoft Windows Media Player winmm.dll MIDI 文件堆溢出漏洞调试分析

    0x01 蜘蛛漏洞攻击包 前言:2012 年 2月,地下黑产中流行着一款国产名为蜘蛛漏洞的攻击包 -- "Zhi-Zhu Exploit Pack",该工具包含 5 个漏洞,都是在 ...

  5. 【STM32H7教程】第32章 STM32H7的TIM定时器基础知识和HAL库API

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第32章       STM32H7的TIM定时器基础知识和H ...

  6. windows 自带winmm.dll播放音频问题

    同事用的一个录音小程序在他机器上可以用,换了两个电脑不能用,获取音频长度时总是0,检查代码也没有发现具体问题.最后发现是电脑声卡驱动的问题.更新声卡驱动好了. 附上播放音频的代码: 首先,导入dll文 ...

  7. STM32 HAL库学习系列第4篇 定时器TIM----- 开始定时器与PWM输出配置

    基本流程: 1.配置定时器 2.开启定时器 3.动态改变pwm输出,改变值  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); 函数总结: __HAL_TIM ...

  8. golang调用c++的dll库文件

    最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ...

  9. JNA 如何 加载多个 存在依赖的 DLL 库

    JNA 的出现,极大的简化了原有的 JNI 技术.下面是JNA github地址:https://github.com/java-native-access/jna 1. 简单的一个例子: /** S ...

随机推荐

  1. QGIS1.8.0的编译

    很早就关注QGIS了,关于它的编译,也尝试了好几次,但都没有成功.在要放弃的时候,再尝试了一回,完全按照他的intall指导.终于成功. 择其要点而言,就是要按部就班,不能偷工减料.想要成功编译,请按 ...

  2. Python爬虫(十)_正则表达式

    本篇将介绍python正则表达式,更多内容请参考:[python正则表达式] 什么是正则表达式 正则表达式,又称规则表达式,通常被用来检索.替换那些符合某个模式(规则)的文本. 正则表达式是对字符串操 ...

  3. CSS clear 清除浮动,兼容各浏览器

    .clear:after{content:".";display:block;height:0;clear:both;visibility:hidden;} .clear{zoom ...

  4. Android中使用ContentProvider进行跨进程方法调用

    原文同一时候发表在我的博客 点我进入还能看到很多其它 需求背景 近期接到这样一个需求,须要和别的 App 进行联动交互,比方下载器 App 和桌面 App 进行联动.桌面的 App 能直接显示下载器 ...

  5. php&amp;&amp;页面静态化

    页面静态化.主要是出于两个方面的考虑.     第一:訪问html页面的速度比訪问php页面的速度快.在訪问php页面时候.须要对php进行解析.訪问html时候,直接浏览器能够解析出来.特别是PV量 ...

  6. 解决IOS微信内置浏览器返回后不执行js脚本的问题

    在A页面写一个$(function(){}) 后随便点击一个URL跳转到B页面 利用微信内置浏览器 返回键返回到A页面后发现这段JS不执行,后来找到了解决方案 $(function () { var ...

  7. javascript中的事件Event

    一.事件流 1.事件流:描述的是从页面中接受事件的顺序 IE的事件流是事件冒泡流,Netscape的事件流是事件捕获流. 2.事件冒泡 IE的事件流叫做事件冒泡(event bubbing),即事件开 ...

  8. DataBase MongoDB高级知识-易使用

    MongoDB高级知识-易使用 mongodb是一个面向文档的数据库,而不是关系型数据库.不采用关系模型主要是为了获取更好的扩展性.当然还有其他的一些好处. 与关系型数据库相比,面向文档的数据库不再有 ...

  9. bzoj 3242: [Noi2013]快餐店

    Description 小T打算在城市C开设一家外送快餐店.送餐到某一个地点的时间与外卖店到该地点之间最短路径长度是成正比的,小T希望快餐店的地址选在离最远的顾客距离最近的地方. 快餐店的顾客分布在城 ...

  10. CJOJ 免费航班

    Description 小Z在MOI比赛中获得了大奖,奖品是一张特殊的机 票.使用这张机票,可以在任意一个国家内的任意城市之间的免费飞行,只有跨国飞行时才会有额外的费用.小Z获得了一张地图,地图上有城 ...