Raise Event in High Resolution Interval/Timer

Raise event in high resolution interval/timer

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.

How to raise only 1 Timer event in C#?

Usually what I do is have my event stop the timer when it's raised and then restart the timer when the event process completes:

private void timerHandler(object sender, TimerElapsedEventArgs e)
{
Timer timer = (Timer)sender;
timer.Stop();
RunProcess();
timer.Start();
}

public void RunProcess()
{
/* Do stuff that takes longer than my timer interval */
}

Now my timer will start again on completion of the process

High resolution timer in C#

There is nothing built into the .NET framework that I am aware of. Windows has a mechanism for high resolution timer events via the Multimedia Timer API. Below is a quick example I whipped up which seems to do the job. There are also seems to be a good example here.

I will note that this API changes system wide settings that can degrade system performance, so buyer beware. For testing purposes, I would recommend keeping track of how often the timer is firing to verify the timing is similar to the device you are trying to simulate. Since windows is not a real-time OS, the load on your system may cause the MM timer be delayed resulting in gaps of 100 ms that contain 100 events in quick succession, rather than 100 events spaced 1 ms apart. Some additional reading on MM timers.

class Program
{
static void Main(string[] args)
{
TestThreadingTimer();
TestMultimediaTimer();
}

private static void TestMultimediaTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new MultimediaTimer() { Interval = 1 })
{
timer.Elapsed += (o, e) => Console.WriteLine(s.ElapsedMilliseconds);
s.Start();
timer.Start();
Console.ReadKey();
timer.Stop();
}
}

private static void TestThreadingTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new Timer(o => Console.WriteLine(s.ElapsedMilliseconds), null, 0, 1))
{
s.Start();
Console.ReadKey();
}
}

}

public class MultimediaTimer : IDisposable
{
private bool disposed = false;
private int interval, resolution;
private UInt32 timerId;

// Hold the timer callback to prevent garbage collection.
private readonly MultimediaTimerCallback Callback;

public MultimediaTimer()
{
Callback = new MultimediaTimerCallback(TimerCallbackMethod);
Resolution = 5;
Interval = 10;
}

~MultimediaTimer()
{
Dispose(false);
}

public int Interval
{
get
{
return interval;
}
set
{
CheckDisposed();

if (value < 0)
throw new ArgumentOutOfRangeException("value");

interval = value;
if (Resolution > Interval)
Resolution = value;
}
}

// Note minimum resolution is 0, meaning highest possible resolution.
public int Resolution
{
get
{
return resolution;
}
set
{
CheckDisposed();

if (value < 0)
throw new ArgumentOutOfRangeException("value");

resolution = value;
}
}

public bool IsRunning
{
get { return timerId != 0; }
}

public void Start()
{
CheckDisposed();

if (IsRunning)
throw new InvalidOperationException("Timer is already running");

// Event type = 0, one off event
// Event type = 1, periodic event
UInt32 userCtx = 0;
timerId = NativeMethods.TimeSetEvent((uint)Interval, (uint)Resolution, Callback, ref userCtx, 1);
if (timerId == 0)
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}

public void Stop()
{
CheckDisposed();

if (!IsRunning)
throw new InvalidOperationException("Timer has not been started");

StopInternal();
}

private void StopInternal()
{
NativeMethods.TimeKillEvent(timerId);
timerId = 0;
}

public event EventHandler Elapsed;

public void Dispose()
{
Dispose(true);
}

private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
var handler = Elapsed;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}

private void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException("MultimediaTimer");
}

private void Dispose(bool disposing)
{
if (disposed)
return;

disposed = true;
if (IsRunning)
{
StopInternal();
}

if (disposing)
{
Elapsed = null;
GC.SuppressFinalize(this);
}
}
}

internal delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);

internal static class NativeMethods
{
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
internal static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType);

[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
internal static extern void TimeKillEvent(UInt32 uTimerId);
}

High resolution timer

I found a solution to this problem in the following blog:
http://web.archive.org/web/20110910100053/http://www.indigo79.net/archives/27#comment-255

It tells you how to use the multimedia timer to have a timer with high frequency. It is working just fine for me!!!

Need a reliable timer to fire an event 192 times per second

You're asking for an event every 5ms and the .NET timers are simply not reliable at this resolution

http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=815

From the article:

The conclusion I drew from all my testing is that, at best, you can count on a timer to tick within 15 milliseconds of its target time. It's rare for the timer to tick before its target time, and I never saw it be early by more than one millisecond. The worst case appears to be that the timer will tick within 30 milliseconds of the target time. Figure the worst case by taking your desired target frequency (i.e. once every 100 milliseconds), rounding up to the next multiple of 15, and then adding 15. So, absent very heavy CPU load that prevents normal processing, a 100 ms timer will tick once every 99 to 120 ms.

You definitely can't get better resolution than 15 milliseconds using these timers. If you want something to happen more frequently than that, you have to find a different notification mechanism. No .NET timer object will do it.

There are ways to get this resolution but they typically involve specific hardware designed for high-frequency timing applications and driver interop for their events. I've done this before using an acousto-optic modulator, laser source and CCD.

System.Timers.Timer steadily increasing the interval

You can base it off of the time until the next second (e.g. if there is only 890 milliseconds until the next second on the dot) and restart the timer with that interval every iteration to prevent drifting. Adapted from a previous answer by Jared here. This does not offer precision to the exact millisecond, but it will prevent your times from drifting and the event will always fire "on the dot".

Edit: Removed an unnecessary line - You actually don't need to call Start() twice, just changing the interval is sufficient since changing the interval restarts the timer.

Edit 2: Made some changes to make it more accurate in edge cases (e.g. prevent firing multiple times in a row).

public class Meter
{
private Timer ReadingTime;
private DateTime NextTickTimeWholeSeconds;

public Meter() {
DateTime now = DateTime.UtcNow;
NextTickTimeWholeSeconds = new DateTime(now.Ticks - (now.Ticks % TimeSpan.TicksPerSecond), now.Kind);

ReadingTime = new Timer();
ReadingTime.AutoReset = false;
ReadingTime.Elapsed += new ElapsedEventHandler(PerformReading);
ReadingTime.Interval = GetTimeToNextSecond();
}

public void StartMeter()
{
ReadingTime.Start();
}

private double GetTimeToNextSecond()
{
NextTickTimeWholeSeconds = NextTickTimeWholeSeconds.AddSeconds(1);
var interval = NextTickTimeWholeSeconds - DateTime.UtcNow;
return interval.Milliseconds < 1 ? GetTimeToNextSecond() : interval.Milliseconds;
}

private void PerformReading(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Performing reading: " + DateTime.Now.Hour + ":" + DateTime.Now.Minute + ":" + DateTime.Now.Second + "." + DateTime.Now.Millisecond);
ReadingTime.Interval = GetTimeToNextSecond();
}
}

Example Output:

Performing reading: 18:11:47.10
Performing reading: 18:11:48.4
Performing reading: 18:11:49.10
Performing reading: 18:11:50.6
Performing reading: 18:11:51.9
Performing reading: 18:11:52.5
Performing reading: 18:11:53.10
Performing reading: 18:11:54.5
Performing reading: 18:11:55.9
Performing reading: 18:11:56.7
Performing reading: 18:11:57.7
Performing reading: 18:11:58.7
Performing reading: 18:11:59.7
Performing reading: 18:12:0.8
Performing reading: 18:12:1.7
Performing reading: 18:12:2.6

... about 50 seconds later ...

Performing reading: 18:12:50.1
Performing reading: 18:12:51.0
Performing reading: 18:12:52.0
Performing reading: 18:12:53.0
Performing reading: 18:12:53.999
Performing reading: 18:12:55.0
Performing reading: 18:12:56.0
Performing reading: 18:12:56.999
Performing reading: 18:12:58.0
Performing reading: 18:12:59.0
Performing reading: 18:13:0.0
Performing reading: 18:13:0.999
Performing reading: 18:13:2.0
Performing reading: 18:13:3.1
Performing reading: 18:13:4.0
Performing reading: 18:13:5.0
Performing reading: 18:13:6.0
Performing reading: 18:13:6.999
Performing reading: 18:13:8.0
Performing reading: 18:13:9.0
Performing reading: 18:13:10.0
Performing reading: 18:13:11.0
Performing reading: 18:13:12.1
Performing reading: 18:13:13.0
Performing reading: 18:13:13.999
Performing reading: 18:13:15.0
Performing reading: 18:13:16.0
Performing reading: 18:13:16.999
Performing reading: 18:13:18.0
Performing reading: 18:13:19.1
Performing reading: 18:13:20.0
Performing reading: 18:13:21.0
Performing reading: 18:13:21.999
Performing reading: 18:13:23.0
Performing reading: 18:13:24.0
Performing reading: 18:13:25.0
Performing reading: 18:13:26.0
Performing reading: 18:13:27.0
Performing reading: 18:13:28.0
Performing reading: 18:13:29.0

How to create a high resolution timer in Linux to measure program performance?

Check out clock_gettime, which is a POSIX interface to high-resolution timers.

If, having read the manpage, you're left wondering about the difference between CLOCK_REALTIME and CLOCK_MONOTONIC, see Difference between CLOCK_REALTIME and CLOCK_MONOTONIC?

See the following page for a complete example: http://www.guyrutenberg.com/2007/09/22/profiling-code-using-clock_gettime/

#include <iostream>
#include <time.h>
using namespace std;

timespec diff(timespec start, timespec end);

int main()
{
timespec time1, time2;
int temp;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1);
for (int i = 0; i< 242000000; i++)
temp+=temp;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2);
cout<<diff(time1,time2).tv_sec<<":"<<diff(time1,time2).tv_nsec<<endl;
return 0;
}

timespec diff(timespec start, timespec end)
{
timespec temp;
if ((end.tv_nsec-start.tv_nsec)<0) {
temp.tv_sec = end.tv_sec-start.tv_sec-1;
temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
} else {
temp.tv_sec = end.tv_sec-start.tv_sec;
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
}
return temp;
}


Related Topics



Leave a reply



Submit