以高分辨率间隔/计时器提升事件

时间:2010-11-18 07:40:48

标签: c# timer stopwatch

我想使用c#使用最高分辨率计时器。例如,我想每11个刻度提升一个事件(我听说刻度是pc中最高的计数器)。我试过计时器,发现最小经过时间以毫秒为单位。我看了秒表,但秒表不会引发事件。

感谢。

3 个答案:

答案 0 :(得分:18)

使用多媒体计时器每秒可以提供大约1000个事件。这段代码可以帮助你。

     public delegate void TimerEventHandler(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);

    /// <summary>
    /// A multi media timer with millisecond precision
    /// </summary>
    /// <param name="msDelay">One event every msDelay milliseconds</param>
    /// <param name="msResolution">Timer precision indication (lower value is more precise but resource unfriendly)</param>
    /// <param name="handler">delegate to start</param>
    /// <param name="userCtx">callBack data </param>
    /// <param name="eventType">one event or multiple events</param>
    /// <remarks>Dont forget to call timeKillEvent!</remarks>
    /// <returns>0 on failure or any other value as a timer id to use for timeKillEvent</returns>
    [DllImport("winmm.dll", SetLastError = true,EntryPoint="timeSetEvent")]
    static extern UInt32 timeSetEvent(UInt32 msDelay, UInt32 msResolution, TimerEventHandler handler, ref UInt32 userCtx, UInt32 eventType);

    /// <summary>
    /// The multi media timer stop function
    /// </summary>
    /// <param name="uTimerID">timer id from timeSetEvent</param>
    /// <remarks>This function stops the timer</remarks>
    [DllImport("winmm.dll", SetLastError = true)]
    static extern void timeKillEvent(  UInt32 uTimerID );

运行它们后,请停止这些计时器。它们对你的系统非常沉重*。捕获所有异常并且不要让它们逃脱您的事件处理程序。

*启动超过5个计时器将严重降低大多数系统的速度! 在事件处理程序中尽可能少地执行代码,并确保执行代码快于1毫秒或遇到严重问题。我每隔10-50滴就开始一次代表,以增加标签显示。

Thread.Sleep上发生的正常线程切换将使一个线程槽无需代码,大约需要40毫秒。您也可以通过一些NT内核调用来增加线程切换频率,但请不要这样做。

答案 1 :(得分:14)

首先,您需要意识到由于硬件和软件都有限制,因此在计算机上进行精确计时非常困难(如果不是不可能)。好消息是这个很少需要那种精确度。十个刻度是非常小的时间量。在这段时间内,CPU完成的工作很少,而且从来没有统计意义。

作为参考,Windows时钟的精度约为10毫秒(早期版本较少)。通过调用DateTime.UtcNow来包装您的代码不会比这更好。

在你的问题中,你谈到想要“举办活动”。问题是,以特定间隔引发事件的唯一类型的计时对象是Timer对象。它在.NET Framework中有3种不同的版本(System.Timers.TimerSystem.Threading.TimerSystem.Windows.Forms.Timer),所有这些都有自己独特的使用场景和相对怪癖,但 none它们可以保证在任何接近您要求的精度。它们甚至不是为了这样做而设计的,也没有任何Windows API公开的等效函数可以提供这种类型的精度。

我问你为什么要这样做以及你是否尝试进行基准测试的原因是因为这会改变整个游戏。 .NET Framework(从2.​​0版开始)提供了Stopwatch object,它专门用于准确测量基准测试或性能分析等情况的已用时间。 Stopwatch只包含Windows API函数QueryPerformanceFrequencyQueryPerformanceCounter(这应该确认我对其预期用途的建议)。我们以前必须使用P / Invoke这些函数来在早期版本的Framework中访问这种类型的功能,但它现在已经方便地内置了。如果你需要一个具有相对较高分辨率的计时器来进行基准测试,{{1是你最好的选择。从理论上讲,它可以为你提供亚微秒的时间。

但它并非没有问题。它不会引发任何事件,因此如果您当前的设计依赖于事件处理,那么您将不得不重新考虑它。并且,也不能保证完全准确。当然,鉴于硬件限制,它可能具有最高分辨率,但这并不意味着它必然会满足您的要求规定的要求。例如,在必须在同一处理器上执行StopwatchStart的多处理器系统上,它可能不可靠。这应该没关系,但是it does。处理器上的subject to being unreliable也可以上下调节时钟速度。我甚至敢提到,Stop本身的呼叫需要花费一些时间 - 即使在现代的2+ GHz处理器上大约需要5微秒,这会阻止你实际上能够实现那个响起的亚微秒时序理论上很好。然后,任何合理的代码分析器都会认为这段时间可以忽略不计,因为它是
(另见:http://www.devsource.com/c/a/Techniques/High-Performance-Timing-under-Windows/2/

答案 2 :(得分:4)

各种计时器类使用更大的粒度。 Threading.Timer和Timers.Timer都使用1/64秒,即15.625毫秒。

如果你所指的'tick'是100纳秒刻度,由DateTime类,TimeSpan类使用,并由秒表输出,那么你要问的11刻度长度是1,100纳秒,或1.1微秒。据我所知,没有内置计时器可以为您提供该分辨率。如果你确实希望事件每1.1微秒发生一次,你将不得不删除“计时器”的想法,而是考虑一个短暂的延迟。使线程具有高优先级,并在循环中运行您的事件。不要调用Thread.Sleep(),因为我认为1.1微秒小于系统调度程序的时间片。你需要做一个延迟循环。

另外,要意识到你所询问的时间间隔非常非常小。在2 GHz处理器上,1.1微秒只有2,200个处理器周期。不是一个微不足道的数量,但没有很多时间来完成大量的工作。如果你正在谈论你在评论中所说的1个滴答,那只有200个处理器周期:那是足够的时间来做几十个数学运算,也许可以调用一个函数。