项目里面用到的这些看起来名字高大上的定时器测试下来也是非常不准。看了源码发现也是用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. Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)

    Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效) 前言 今天在使用Spring cache的Cacheable注解的过程中遇见了一个Cacheable注解失效的问题, ...

  2. 》》QQ-注册

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. java 可变參数

    我们在某些特定的需求环境下,可能要对某一个方法中的參数进行一些操作,并且这些方法中的參数是不规定的,那么问题来了,我们该怎么办呢? java事实上就为我们考虑了这样的情况,那就是使用可变參数 可变參数 ...

  4. 天津政府应急系统之GIS一张图(arcgis api for flex)解说(三)显示地图坐标系模块

    config.xml文件的配置例如以下: 1 2 <widget left="3" bottom="3" config="widgets/Coo ...

  5. 被动式Telnet研究及实现(解决内外网远程维护的困难)-part A

    欢迎转载.转载请保留原文链接:http://blog.csdn.net/mikulee/article/details/40149779 项目背景及需求: 近期公司有一个项目: 我们在一个arm主机上 ...

  6. windows下wamp多域名的配置

    前面的话 本文将详细介绍windows下wamp多域名的配置方法 修改hosts文件 hosts文件的位置:系统盘→“windows”→“System32”→“drivers”→“etc” 打开hos ...

  7. Struts2.5 伪静态的配置

    伪静态 伪静态是相对真实静态来讲的,通常我们为了增强搜索引擎的友好面,都将文章内容生成静态页面,但是有的朋友为了实时的显示一些信息.或者还想运用动态脚本解决一些问题.不能用静态的方式来展示网站内容.但 ...

  8. 自学Python2.5-基本数据类型-set集合

    Python set集合 一. set集合概述 ①set集合,是一个无序且不重复的元素集合.②集合对象是一组无序排列的可哈希的值,集合成员可以做字典中的键.③集合支持用in和not in操作符检查成员 ...

  9. Java加密与解密笔记(四) 高级应用

    术语列表: CA:证书颁发认证机构(Certificate Authority) PEM:隐私增强邮件(Privacy Enhanced Mail),是OpenSSL使用的一种密钥文件. PKI:公钥 ...

  10. 自学WPF之Binding(二)

    没有Source的Binding,使用ContentText作为Binding源: 上一篇是把CLR类型对象当作指定为Binding的Source,两种方法:一是把对象赋值给Binding.Sourc ...