Timer计时不准确的问题及解决方法
在项目中,需要每隔20ms发送一个RTP数据包。一开始使用的是System.Windows.Forms下的Timer类,但是发现明显延迟了。用StopWatch测了一下,发现它的触发间隔居然不是20ms,而是在31ms左右摇摆。换了System.Threading下的Timer和System.Timers下和Timer也不行,一样的问题。
为什么会这样呢?在网上发现了一段非常具有启发性的话,它解释了原因并给出了解决的办法:
目前,Windows软件一般使用Timer定时器进行定时。Timer定时器是由应用程序响应定时消息WM_TIMER实现定时。Timer定时器是IBM PC硬件和ROM BIOS构造的定时器的简单扩充。PC的ROM初始化8253定时器来产生硬件中断08H,而08H中断的频率为18.2Hz,即至少每隔54.925 ms中断一次。此外,这个定时消息的优先权太低,只有在除WM_PAINT外的所有消息被处理完后,才能得到处理。
多媒体定时器也是利用系统定时器工作的,但它的工作机理和普通定时器有所不同。首先,多媒体定时器可以按精度要求设置8253的T/C0通道的计数初值,使定时器不存在54.945ms的限制;其次,多媒体定时器不依赖于消息机制,而是用函数产生一个独立的线程,在一定的中断次数到达后,直接调用预先设置好的回调函数进行处理,不必等到应用程序的消息队列为空,从而切实保障了定时中断得到实时响应,使其定时精度可达1ms。
这段话中的多媒体定时器被放在了winmm.dll中。要使用这个定时器,可以通过调用timeSetEvent和timeKillEvent函数来实现。为了便于使用,参照网上的一些资料,对这两个方法进行了封装。现跟大家分享一下。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.ComponentModel; 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( !this.isRunning )
{
this.timerID = timeSetEvent( this.interval, this.resolution, this.timerCallback, , ); // 间隔性地运行 if( this.timerID == )
{
throw new Exception( "无法启动计时器" );
}
this.isRunning = true;
}
} /// <summary>
///
/// </summary>
public void Stop()
{
if( this.isRunning )
{
timeKillEvent( this.timerID );
this.isRunning = false;
}
} /// <summary>
/// 实现IDisposable接口
/// </summary>
public void Dispose()
{
timeKillEvent( this.timerID );
GC.SuppressFinalize( this );
EventHandler disposed = this.Disposed;
if( disposed != null )
{
disposed( 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 );
// The timeGetDevCaps function queries the timer device to determine its resolution. private void TimerEventCallback( int id, int msg, int user, int param1, int param2 )
{
if( this.Tick != null )
{
this.Tick( 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;
} }
调用方法:
MillisecondTimer timer1; private void button1_Click( object sender, EventArgs e )
{
timer1 = new MillisecondTimer();
timer1.Interval = ;
timer1.Tick += new EventHandler( timer1_Tick );
timer1.Start();
} int i;
void timer1_Tick( object sender, EventArgs e )
{
Console.WriteLine( i++ );
} private void button2_Click( object sender, EventArgs e )
{
timer1.Stop();
timer1.Dispose();
}
参考:
Timer计时不准确的问题及解决方法的更多相关文章
- Timer计时不准确的解决方案 每次都重新调整,修正误差
http://stackoverflow.com/questions/29722838/system-timers-timer-steadily-increasing-the-interval 需要在 ...
- php浮点数计算比较及取整不准确解决方法
原文:php浮点数计算比较及取整不准确解决方法 php有意思的现象,应该是很多编程语言都会有这样的现象.这个是因为计算机的本身对浮点数识别的问题..... $f = 0.58; var_dump(in ...
- 阿里云 ECS centos java timer进程异常/混乱......的解决方法
之前就知道timer进程长久运行容易出问题,所以一直对timer进行了很长一段时间的日志监控和数据库记录,大概观察了几个月,没发现过问题....然后就没管理了,数据库记录也没做了,昨天这问题就来了,t ...
- ExtJs4.2中Tab选项卡的右击关闭其它和关闭当前功能不准确的解决方法
一.ExtJs4.2中Tab选项卡的右击关闭其它和关闭当前功能不准确的解决方法 二.找到ux目录下的TabCloseMenu.js文件,将内容替换成下面代码. 三.代码: /** * Plugin f ...
- 小程序 RecorderManager计时不准确问题
官方文档:RecorderManager 录音管理器,内部实现计时不准确.有以下俩个问题: 点击暂停继续,当录音结束时,stop返回的时间包含了暂停的那一段时间. 正常录音,录音文件的时长有概率少个1 ...
- window.onresize 多次触发的解决方法
用了window.onresize但是发现每次 onresize 后页面中状态总是不对,下面与大家分享下onresize 事件多次触发的解决方法. 之前做一个扩展,需要在改变窗口大小的时候保证页面显示 ...
- MVVM框架从WPF移植到UWP遇到的问题和解决方法
MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...
- Sublime Text 无法使用Package Control的解决方法 以及 常用的插件安装过程
大概一个月之前给 Macbook air 装 Sublime Text 3 的时候,遇到过这个问题,当时解决了,现在回想,感觉忘的七七八八了,赶紧趁着还没有全忘光的时候记下来,当时的过程记得不一定准确 ...
- android手机出现sqlite3 not found的解决方法
解决方法如下: 1.如果/system目录为不可读写的,需要挂载为读写: C:\Users\easteq>adb shell root@android:/ # mount -o remount, ...
随机推荐
- cglib之Enhancer
1. 背景 cglib库的Enhancer在Spring AOP中作为一种生成代理的方式被广泛使用.本文针对Enhancer的用法以实际代码为例作一些介绍. 2. Enhancer是啥 Enhance ...
- Java面试之五大框架的理解
五大框架(springMVC,struts2,spring,mybatis,hibernate) 说说你对springMVC框架的理解? 简要口述(如果感觉说的少可以在完整答案里面挑几条说) Spri ...
- Address already in use: JVM_Bind问题的解决
tomcat一般出现Address already in use: JVM_Bind的问题,可能是端口冲突,也就是端口被占用了. 这个可能是其他应用程序使用了同一个端口(默认是8080),也可能是你启 ...
- 布局:高度已知,布局一个三栏布局,左栏和右栏宽度为200px,中间自适应
需求:高度已知为200px,写出三栏布局,左栏和右栏各位200px,中间自适应,如下图所示: 方法一:float浮动布局 原理是:定义三个区块,需要注意的是中间的区块放在右边区块的下面,统一设置高度为 ...
- 分布式消息列队RocketMQ部署
模式: 多Master多Slave模式,异步复制: 每个 Master 配置一个 Slave,有多对Master-Slave,HA 采用异步复制方式,主备有短暂消息延迟,毫秒级. 优点:即使磁盘损坏, ...
- luogu P4718 【模板】Pollard-Rho算法(贴代码)
嘟嘟嘟 从标题中能看出来,我只是想贴一个代码. 先扯一会儿. 前几天模拟考到了这东西,今天有空就学了一下. 到网上找资料,发现前置技能是miller-rabin筛法,于是我还得先学这么个东西. 学mi ...
- !important的用法
如果写成:a{color:teal !important},IE6和其它高版本浏览器都认识,(IE6虽然不认识!important,但它还是能够认识!important之前的color:teal这个属 ...
- Qt 编程指南 9 软件打包
Qt 官方开发环境使用的动态链接库方式,在发布生成的exe程序时,需要复制一大堆 dll,如果自己去复制dll,很可能丢三落四,导致exe在别的电脑 里无法正常运行.因此 Qt 官方开发环境里自带了一 ...
- 【Codeforces 331D3】Escaping on Beaveractor
题意:给\(b\times b\)的网格,其中有\(n\)个不交叉的箭头. 现在有\(q\)个询问,每个询问包含一个点\((x,y)\),以及一个方向\(dir\).时间\(t\). 要求从\((x, ...
- python:利用smtplib模块发送邮件
自动化测试中,测试报告一般都需要发送给相关的人员,比较有效的一个方法是每次执行完测试用例后,将测试报告(HTML.截图.附件)通过邮件方式发送. 参考代码:send_mail.py 一.python对 ...