Microsecond and Millisecond C# Timer[转]

文章转至:http://www.codeproject.com/Articles/98346/Microsecond-and-Millisecond-NET-Timer

Introduction
Anyone who has used the .NET System.Timers.Timer class for low interval times will realise that it does not offer a very high resolution. The resolution will be system dependant, but a maximum resolution is usually around 15ms (System.Windows.Forms.Timer has an even worse resolution, although it is unlikely a UI will need to be updated this fast). Significantly better performance can be achieved using the Win32 multimedia timer (there are various .NET projects that wrap this timer); however, there are no timers available in the microsecond range.

The problem I encountered was that I needed to send an Ethernet UDP message packet out every 800µs (0.8ms); it did not matter if a packet was slightly delayed or did not go off exactly 800µs after the last one. Basically, what I needed was a microsecond timer that was accurate the majority of the time.

The fundamental problem with a software timer in the region of 1ms is that Windows is a non real-time Operating System (RTOS) and is not suitable for generating regular and accurate events around the 1ms mark. MicroTimer cannot and does not solve this problem; however, it does offer a microsecond timer which offers a reasonable degree of accuracy (approx. 1µs) the majority (approx. 99.9%) of the time. The trouble is, the 0.1% of the time, the timer can be very inaccurate (whilst the Operating System gives some of the processing time to other threads and processes). The accuracy is highly system/processor dependant; a faster system will result in a more accurate timer.

The beauty of MicroTimer is that it is called in a very similar way to the existing System.Timers.Timer class; however, the interval is set in microseconds (as opposed to milliseconds in System.Timers.Timer). On each timed event, MicroTimer invokes the predefined (OnTimedEvent) callback function. The MicroTimerEventArgs properties provide information (to the microsecond) on when exactly (and how late) the timer was invoked.
Using the code

‘MicroLibrary.cs‘ encompasses three classes (under the namespace MicroLibrary):

MicroStopwatch - This derives from and extends the System.Diagnostics.Stopwatch class; importantly, it provides the additional property ElapsedMicroseconds. This is useful as a standalone class where the elapsed microseconds from when the stopwatch was started can be directly obtained.
    MicroTimer - Designed so it operates in a very similar way to the System.Timers.Timer class, it has a timer interval in microseconds and Start / Stop methods (or Enabled property). The timer implements a custom event handler (MicroTimerElapsedEventHandler) that fires every interval. The NotificationTimer function is where the ‘work‘ is done and is run in a separate high priority thread. It should be noted that MicroTimer is inefficient and very processor hungry as the NotificationTimer function runs a tight while loop until the elapsed microseconds is greater than the next interval. The while loop uses a SpinWait, this is not a sleep but runs for a few nanoseconds and effectively puts the thread to sleep without relinquishing the remainder of its CPU time slot. This is not ideal; however, for such small intervals, this is probably the only practical solution.
    MicroTimerEventArgs - Derived from System.EventArgs, this class provides an object for holding information about the event. Namely, the number of times the event has fired, the absolute time (in microseconds) from when the timer was started, how late the event was and the execution time of the callback function (for the previous event). From this data, a range of timer information can be derived.

By design, the amount of work done in the callback function (OnTimedEvent) must be small (e.g. update a variable or fire off a UDP packet). To that end, the work done in the callback function must take significantly less time than the timer interval. Separate threads could be spawned for longer tasks; however, this goes outside the scope of this article. As discussed earlier, because Windows is not a real time Operating System, the callback function (OnTimedEvent) may be late; if this happens and any particular interval is delayed, there are two options:

Either: Set the property IgnoreEventIfLateBy whereby the callback function (OnTimedEvent) will not be called if the timer is late by the specified number of microseconds. The advantage of this is the timer will not attempt to ‘catch up‘, i.e., it will not call the callback function in quick succession in an attempt to catch up. The disadvantage is that some events will be missed.
    Or: By default, MicroTimer will always try and catch up on the next interval. The advantage of this is the number of times the OnTimeEvent is called will always be correct for the total elapsed time (which is why the OnTimedEvent must take significantly less time than the interval; if it takes a similar or longer time, MicroTimer can never ‘catch up‘ and the timer event will always be late). The disadvantage of this is when it‘s trying to ‘catch up‘, the actual interval achieved will be much less than the required interval as the callback function is called in quick succession in an attempt to catch up.

The timer may be stopped in one of three ways:

Stop (or Enabled = false) - This method stops the timer by setting a flag to instruct the timer to stop, however, this call executes asynchronously i.e. the call to Stop will return immediately (but the current timer event may not have finished).
    StopAndWait - This method stops the timer synchronously, it will not return until the current timer (callback) event has finished and the timer thread has terminated. StopAndWait also has an overload method that accepts a timeout (in ms), if the timer successfully stops within the timeout period then true is returned, else false is returned.
    Abort - This method may be used as a last resort to terminate the timer thread, for example, to abort the timer if it has not stopped after waiting 1sec (1000ms) use:
    if( !microTimer.StopAndWait(1000) ){ microTimer.Abort(); }

The code below shows the MicroLibrary namespace (MicroLibrary.cs) which contains the three classes, MicroStopwatch, MicroTimer and MicroTimerEventArgs. See the ‘Download source‘ link above.

The code below shows a very simple (console application) implementation of the MicroTimer class with the interval set to 1,000µs (1ms). See the ‘Download Console demo project‘ link above.

The screenshot below shows the console output. The performance varies on different runs, but was usually accurate to 1µs. Due to system caching, the accuracy was worse on the first run and got better after the first few events. This test was on a 2GHz Dell Inspiron 1545 with an Intel Core 2 Duo (running Windows 7 64bit). The performance improved significantly on faster machines.

It is very unlikely a UI will need to be updated at intervals in the millisecond range. Purely for the point of demonstration, the ‘Download WinForms demo project‘ link above contains a very simple WinForms application that updates a UI using the MicroTimer. The screenshot below demonstrates the application acting as a stopwatch (with a microsecond display) where the UI is being updated with the ElapsedMicroseconds every 1111µs (1.111ms).

Summary

MicroTimer is designed for situations were a very quick timer is required (around the 1ms mark); however, due to the non real-time nature of the Windows Operating System, it can never be accurate. However, as no other microsecond software timers are available, it does offer a reasonable solution for this task (and although processor hungry, is reasonably accurate on fast systems).

时间: 2024-12-05 07:27:29

Microsecond and Millisecond C# Timer[转]的相关文章

精确到1ms的Timer

在项目中,需要每隔20ms发送一个RTP数据包.一开始使用的是System.Windows.Forms下的Timer类,但是发现明显延迟了.用StopWatch测了一下,发现它的触发间隔居然不是20ms,而是在31ms左右摇摆.换了System.Threading下的Timer和System.Timers下和Timer也不行,一样的问题. 为什么会这样呢?在网上发现了一段非常具有启发性的话,它解释了原因并给出了解决的办法:     目前,Windows软件一般使用Timer定时器进行定时.Tim

System.Threading.Timer 用法

System.Threading.Timer用法和例子 (1)首先声明Timer变量://一定要声明成局部变量以保持对Timer的引用,否则会被垃圾收集器回收!private System.Threading.Timer timerClose; (2)在上述自动执行代码后面添加如下Timer实例化代码:// Create a timer thread and start ittimerClose = new System.Threading.Timer(new TimerCallback(tim

基于dispatch_after封装YXTimer

本人根据dispatch_after封装了一个定时器,支持block以及代理的方式来激活定时器,适用于对精度要求低,耗时短的地方,高端大气上档次,低调奢华有内涵:) 源码: YXTimer.h 与 YXTimer.m // // YXTimer.h // YXTimer // // Created by YouXianMing on 14-10-2. // Copyright (c) 2014年 YouXianMing. All rights reserved. // #import <Foun

你程序会做饭嘛?我能!

别嘲笑这个标题.我想了很久.有点“投机取巧”的功效吧! 程序当然不能做饭. 之前的我们的系列文章,介绍, 多线程执行,任务派发.定时器执行.脚本加载.程序状态机. 这些都是零零散散,或者说都是模块化介绍,以及模块测试用例. 那么今天我们就来模拟正常程序流程.使用上述的功能性代码完成流程. 当然今天的测试用例程序肯定和做饭有关.今天要做的是模拟一个餐厅的流程. 完成 客人入座 -> 点菜 -> 等待就餐 -> 就餐 -> 等待结账 -> 结账 -> 离开. 期间包括 等待

PostgreSQL数据类型

http://blog.csdn.net/neo_liu0000/article/category/797059 第六章  数据类型 6.1概述 PostgreSQL 提供了丰富的数据类型.用户可以使用 CREATE TYPE 命令在数据库中创建新的数据类型.PostgreSQL 的数据类型被分为四种,分别是基本数据类型.复合数据类型.域和伪类型. 基本数据类型是数据库内置的数据类型,包括integer.char.varchar等数据类型.表6-1列出了PostgreSQL提供的所有基本数据类型

线程进行定时操作

(1)首先声明Timer变量: //一定要声明成局部变量以保持对Timer的引用,否则会被垃圾收集器回收! private System.Threading.Timer timerClose; (2)在上述自动执行代码后面添加如下Timer实例化代码: // Create a timer thread and start it timerClose = new System.Threading.Timer(new TimerCallback(timerCall), this, 5000, 0);

PHP毫秒

php的毫秒是没有默认函数的,但提供了一个microtime()函数,该函数返回包含两个元素,一个是秒数,一个是小数表示的毫秒数,借助此函数,可以很容易定义一个返回毫秒数的函数,例如: /* * 获取时间差,毫秒级 */ function get_subtraction() {     $t1 = microtime(true);     $t2 = microtime(true);     return (($t2-$1)*1000).'ms';} /* * microsecond 微秒 mi

FreeSql (二十九)Lambda 表达式

FreeSql 支持功能丰富的表达式函数解析,方便程序员在不了解数据库函数的情况下编写代码.这是 FreeSql 非常特色的功能之一,深入细化函数解析尽量做到满意,所支持的类型基本都可以使用对应的表达式函数,例如 日期.字符串.IN查询.数组(PostgreSQL的数组).字典(PostgreSQL HStore)等等. IFreeSql fsql = new FreeSql.FreeSqlBuilder() .UseConnectionString(FreeSql.DataType.MySql

关于High-Resolution Timer(了解)

如果一个系统包含高精度性能计数器(HRPC,high-resolution performance counter)则此系统提供高精度定时器.你可以使用API函数QueryPerformanceFrequency来获得HRPC的频率HRPCF,返回值为cps(counts per second).这个依赖于处理器(processor dependent),在一些处理器中HRPCF的值可能就是处理器时钟周期(the cycle rate of the processor clock),比如我们测试