项目里面用到的这些看起来名字高大上的定时器测试下来也是非常不准。看了源码发现也是用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. 小实例---关于input宽度自适应以及多个input框合并拆分

    前两个月,公司内部需要开发关于大数据方面的辅助工具语料分词系统,在这个项目中遇到以下几个主要问题,在此分享~ 一.input宽度根据内定文本宽度自适应 背景:项目需求中,前台展示,需要从后台获取的.t ...

  2. 使用css修改radio、checkbox样式

    input[type=radio],input[type=checkbox]  { display: inline-block; vertical-align: middle; width: 20px ...

  3. json解析—Gson以及GsonFormat插件的运用

    最近开始慢慢做毕业设计了,遇到一个功能是获取天气预报的,我选择的是和风天气的api,返回的是JSON数据,所以遇到了解析JSON的问题 首先简单说下JSON,JSON(JavaScript Objec ...

  4. JSONArray - JSONObject - 遍历 \ 判断object空否

    public static void main(String[] args) { String str = "[{name:'a',value:'aa'},{name:'b',value:' ...

  5. 【剑指offer】扑克牌的顺子

    个大王,2个小王(一副牌原本是54张^_^)...他随机从中抽出了5张牌,想測測自己的手气,看看能不能抽到顺子,假设抽到的话,他决定去买体育彩票,嘿嘿! ."红心A,黑桃3,小王,大王,方片 ...

  6. 项目管理: Alpha,Beta,RC,GA,Release

    Alpha:                    Alpha是内部测试版,一般不向外部发布.也可以认为是演示版本.允许存在一定的问题(例如功能组合.异常流程处理.稳定性.性能存在部分问题)      ...

  7. IDEA快速创建Maven+SpringBoot项目时:Cannot download https://start.spring.io;Status:403

    先展示一下我遇到的问题: 用浏览器搜索是有页面的,但是但是但是呢,用IDEA快速构建的时候就报403 咳咳!巴格虐我万千遍,我待技术如初恋... 我看到的解决办法有以下两种,当然,我只想说:" ...

  8. Android 开发,你遇上 Emoji 头疼吗?

    在 Android 中,如果需要使用的到 Emoji 表情,你会发现在某些设备上,有一些 Emoji 表情会被以豆腐块 "☐" 的形式显示,这是因为当前设备并不支持这个 Emoji ...

  9. vue+express之前后台分离博客

    说来惭愧,这么久没有更新过博客了,写个项目练练手吧,打算写一个vue+express的博客  可能这个时间说长不长说短不短  写到哪就是哪吧  我采用的是前后台分离  express采用的是mvc,但 ...

  10. visual studio 2012打开提示 未能将网站×××配置为使用 ASP.NET 4.5 和 尚未在Web服务器上注册,您需要手动将Web服务器配置为使用ASP.NET 4.5

    未能将网站×××配置为使用 ASP.NET 4.5.为了使此网站正确运行,您必须将它手动配置为使用ASP.NET 4.5. ASP.NET 4.5尚未在Web服务器上注册,您需要手动将Web服务器配置 ...