开发者

Raise event in high resolution interval/timer

开发者 https://www.devze.com 2023-01-26 04:52 出处:网络
I want to use the highest possible resolution timer using c#. For example, I want to raise an event every 11 ticks (I\'ve开发者_Python百科 heard that tick is the highest possible counter in pc). I tri

I want to use the highest possible resolution timer using c#. For example, I want to raise an event every 11 ticks (I've开发者_Python百科 heard that tick is the highest possible counter in pc). I tried timer and found that minimum elapsed time is in milliseconds. I looked at stopwatch but stopwatch doesn't raise events.

Thanks.


Using a multimedia timer should give you about 1000 events per second. This code should help you on the way.

     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 );

Do stop these timers after running them. They are quite heavy on your system*. Do catch all exceptions and don't let them escape your event handler.

*Starting more then 5 timers will seriously slow down most systems! Execute as little as possible code in the event handlers and make sure the executing code is faster then 1 millisecond or face serious problems. I started a delegate every 10-50 ticks to increase a label display.

A normal thread switch that occurs on a Thread.Sleep will leave one thread-slot free of your code and will take about 40 milliseconds. You can also increase the thread switch frequency with some NT kernel calls, but please, don't do that.


First off, you need to realize that it's extremely difficult, if not impossible, to do exact timing on a computer because of limitations exposed both by the hardware and software. The good news is that this kind of precision is rarely necessary. Ten ticks is an insanely small amount of time. Very little work is getting done by the CPU in this interval, and it's never going to be statistically significant.

For reference, the Windows clock has an accuracy of around 10 milliseconds (less on earlier versions). Wrapping your code with calls to DateTime.UtcNow isn't going to do any better than that.

In your question, you talk about wanting to "raise an event." The problem is that the only type of time-keeping object that raises an event at specific intervals is the Timer object. It's available in 3 different incarnations in the .NET Framework (System.Timers.Timer, System.Threading.Timer, and System.Windows.Forms.Timer), all of which have their own unique use scenarios and relative quirks, but none of them guarantee precision anywhere close to what you're asking for. They're not even designed to do so, and neither are there any equivalent functions exposed by the Windows API that are going to provide this type of precision.

The reason I asked why you were wanting to do this and if you're attempting to benchmark is because that changes the entire game. The .NET Framework (as of version 2.0) provides a Stopwatch object that is expressly designed to accurately measure elapsed time for a situation like benchmarking or performance profiling. The Stopwatch simply wraps the Windows API functions QueryPerformanceFrequency and QueryPerformanceCounter (which should confirm my suggestion as to its intended use). We used to have to P/Invoke these functions to access this type of functionality in earlier versions of the Framework, but it's now conveniently built in. If you need a timer with a relatively high resolution for benchmarking, the Stopwatch is your best bet. In theory, it can provide you with sub-microsecond timing.

But it's not without its problems. It doesn't raise any events, so if your current design relies on event handling, you're going to have to rethink it. And, it's not guaranteed to be perfectly accurate, either. Sure, it may have the highest possible resolution given hardware constraints, but that doesn't mean it will necessarily meet your stated requirements. For example, it may be unreliable on a multiple processor system where Start and Stop have to be executed on the same processor. It shouldn't matter, but it does. It's also subject to being unreliable on processors that can throttle their clock speed up and down. And dare I even mention that the call to QueryPerformanceCounter itself is going to take some time—about 5 microseconds even on a modern 2+ GHz processor, which prevents you from actually being able to achieve that sub-microsecond timing that sounded good in theory. Then again, any reasonable code profiler would consider that amount of time negligible because, well, it is.
(See also: http://www.devsource.com/c/a/Techniques/High-Performance-Timing-under-Windows/2/)


The various timer classes use a larger granularity. Both Threading.Timer and Timers.Timer use 1/64 second, which is 15.625 milliseconds.

If the 'tick' you're referring to is the 100 nanosecond tick, used by the DateTime class, TimeSpan class, and output by the Stopwatch, then the 11 tick length you're asking about is 1,100 nanoseconds, or 1.1 microseconds. To the best of my knowledge, there's no built-in timer that will give you that resolution. If you really do want an event to happen every 1.1 microseconds, you're going to have to remove the idea of a 'timer', and instead think in terms of a short delay. Make a thread high priority, and run your event in a loop. Don't call Thread.Sleep(), since I believe 1.1 microseconds is smaller than the timeslice of the system scheduler. You'll need to do a delay loop instead.

Also, realize that the time interval you're asking about is very, very small. 1.1 microseconds is only 2,200 processor cycles on a 2 GHz processor. Not an insignificant amount, but not a lot of time to get much work done. If you're talking the 1 tick that you said in your comment, that's only 200 processor cycles: That's about enough time to do a few dozen math operations, and maybe call one function.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号