Click here to Skip to main content
Click here to Skip to main content

Microsecond and Millisecond C# Timer

By , 14 Apr 2013
 

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.

using System;

namespace MicroLibrary
{
    /// <summary>
    /// MicroStopwatch class
    /// </summary>
    public class MicroStopwatch : System.Diagnostics.Stopwatch
    {
        readonly double _microSecPerTick =
            1000000D / System.Diagnostics.Stopwatch.Frequency;

        public MicroStopwatch()
        {
            if (!System.Diagnostics.Stopwatch.IsHighResolution)
            {
                throw new Exception("On this system the high-resolution " +
                                    "performance counter is not available");
            }
        }

        public long ElapsedMicroseconds
        {
            get
            {
                return (long)(ElapsedTicks * _microSecPerTick);
            }
        }
    }

    /// <summary>
    /// MicroTimer class
    /// </summary>
    public class MicroTimer
    {
        public delegate void MicroTimerElapsedEventHandler(
                             object sender,
                             MicroTimerEventArgs timerEventArgs);
        public event MicroTimerElapsedEventHandler MicroTimerElapsed;

        System.Threading.Thread _threadTimer = null;
        long _ignoreEventIfLateBy = long.MaxValue;
        long _timerIntervalInMicroSec = 0;
        bool _stopTimer = true;

        public MicroTimer()
        {
        }

        public MicroTimer(long timerIntervalInMicroseconds)
        {
            Interval = timerIntervalInMicroseconds;
        }

        public long Interval
        {
            get
            {
                return System.Threading.Interlocked.Read(
                    ref _timerIntervalInMicroSec);
            }
            set
            {
                System.Threading.Interlocked.Exchange(
                    ref _timerIntervalInMicroSec, value);
            }
        }

        public long IgnoreEventIfLateBy
        {
            get
            {
                return System.Threading.Interlocked.Read(
                    ref _ignoreEventIfLateBy);
            }
            set
            {
                System.Threading.Interlocked.Exchange(
                    ref _ignoreEventIfLateBy, value <= 0 ? long.MaxValue : value);
            }
        }

        public bool Enabled
        {
            set
            {
                if (value)
                {
                    Start();
                }
                else
                {
                    Stop();
                }
            }
            get
            {
                return (_threadTimer != null && _threadTimer.IsAlive);
            }
        }

        public void Start()
        {
            if (Enabled || Interval <= 0)
            {
                return;
            }

            _stopTimer = false;

            System.Threading.ThreadStart threadStart = delegate()
            {
                NotificationTimer(ref _timerIntervalInMicroSec,
                                  ref _ignoreEventIfLateBy,
                                  ref _stopTimer);
            };

            _threadTimer = new System.Threading.Thread(threadStart);
            _threadTimer.Priority = System.Threading.ThreadPriority.Highest;
            _threadTimer.Start();
        }

        public void Stop()
        {
            _stopTimer = true;
        }

        public void StopAndWait()
        {
            StopAndWait(System.Threading.Timeout.Infinite);
        }

        public bool StopAndWait(int timeoutInMilliSec)
        {
            _stopTimer = true;

            if (!Enabled || _threadTimer.ManagedThreadId ==
                System.Threading.Thread.CurrentThread.ManagedThreadId)
            {
                return true;
            }

            return _threadTimer.Join(timeoutInMilliSec);
        }

        public void Abort()
        {
            _stopTimer = true;

            if (Enabled)
            {
                _threadTimer.Abort();
            }
        }

        void NotificationTimer(ref long timerIntervalInMicroSec,
                               ref long ignoreEventIfLateBy,
                               ref bool stopTimer)
        {
            int  timerCount = 0;
            long nextNotification = 0;

            MicroStopwatch microStopwatch = new MicroStopwatch();
            microStopwatch.Start();

            while (!stopTimer)
            {
                long callbackFunctionExecutionTime =
                    microStopwatch.ElapsedMicroseconds - nextNotification;

                long timerIntervalInMicroSecCurrent =
                    System.Threading.Interlocked.Read(ref timerIntervalInMicroSec);
                long ignoreEventIfLateByCurrent =
                    System.Threading.Interlocked.Read(ref ignoreEventIfLateBy);

                nextNotification += timerIntervalInMicroSecCurrent;
                timerCount++;
                long elapsedMicroseconds = 0;

                while ( (elapsedMicroseconds = microStopwatch.ElapsedMicroseconds)
                        < nextNotification)
                {
                    System.Threading.Thread.SpinWait(10);
                }

                long timerLateBy = elapsedMicroseconds - nextNotification;

                if (timerLateBy >= ignoreEventIfLateByCurrent)
                {
                    continue;
                }

                MicroTimerEventArgs microTimerEventArgs =
                     new MicroTimerEventArgs(timerCount,
                                             elapsedMicroseconds,
                                             timerLateBy,
                                             callbackFunctionExecutionTime);
                MicroTimerElapsed(this, microTimerEventArgs);
            }

            microStopwatch.Stop();
        }
    }

    /// <summary>
    /// MicroTimer Event Argument class
    /// </summary>
    public class MicroTimerEventArgs : EventArgs
    {
        // Simple counter, number times timed event (callback function) executed
        public int  TimerCount { get; private set; }

        // Time when timed event was called since timer started
        public long ElapsedMicroseconds { get; private set; }

        // How late the timer was compared to when it should have been called
        public long TimerLateBy { get; private set; }

        // Time it took to execute previous call to callback function (OnTimedEvent)
        public long CallbackFunctionExecutionTime { get; private set; }

        public MicroTimerEventArgs(int  timerCount,
                                   long elapsedMicroseconds,
                                   long timerLateBy,
                                   long callbackFunctionExecutionTime)
        {
            TimerCount = timerCount;
            ElapsedMicroseconds = elapsedMicroseconds;
            TimerLateBy = timerLateBy;
            CallbackFunctionExecutionTime = callbackFunctionExecutionTime;
        }

    }
}

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.

using System;

namespace MicroTimerConsoleDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Program program = new Program();
            program.MicroTimerTest();
        }

        private void MicroTimerTest()
        {
            // Instantiate new MicroTimer and add event handler
            MicroLibrary.MicroTimer microTimer = new MicroLibrary.MicroTimer();
            microTimer.MicroTimerElapsed +=
                new MicroLibrary.MicroTimer.MicroTimerElapsedEventHandler(OnTimedEvent);

            microTimer.Interval = 1000; // Call micro timer every 1000µs (1ms)

            // Can choose to ignore event if late by Xµs (by default will try to catch up)
            // microTimer.IgnoreEventIfLateBy = 500; // 500µs (0.5ms)

            microTimer.Enabled = true; // Start timer

            // Do something whilst events happening, for demo sleep 2000ms (2sec)
            System.Threading.Thread.Sleep(2000);

            microTimer.Enabled = false; // Stop timer (executes asynchronously)

            // Alternatively can choose stop here until current timer event has finished
            // microTimer.StopAndWait(); // Stop timer (waits for timer thread to terminate)

            // Wait for user input
            Console.ReadLine();
        }

        private void OnTimedEvent(object sender,
                                  MicroLibrary.MicroTimerEventArgs timerEventArgs)
        {
            // Do something small that takes significantly less time than Interval
            Console.WriteLine(string.Format(
                "Count = {0:#,0}  Timer = {1:#,0} µs, " + 
                "LateBy = {2:#,0} µs, ExecutionTime = {3:#,0} µs",
                timerEventArgs.TimerCount, timerEventArgs.ElapsedMicroseconds,
                timerEventArgs.TimerLateBy, timerEventArgs.CallbackFunctionExecutionTime));
        }
    }
}

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.

MicroTimer/MicroTimerConsoleDemo.jpg

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

MicroTimer/MicroTimerWinFormsDemo.jpg

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

History

  • 31 July 2010 - Article submitted.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

ken.loveday

United Kingdom United Kingdom
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5member Gun Gun Febrianza11-Jun-13 19:56 
this is what i need ! Smile | :)
GeneralMy vote of 5memberDnx_716-May-13 2:32 
Reliable, efficient and simple to use!
QuestionCross-thread operation not validmemberMember 998340612-Apr-13 4:51 
Maybe it was a very noob idea, but I wanted to simply replace my normal timer with microtimer...
So I added the class to my project, I created the timer:
public static MicroLibrary.MicroTimer microtimer1 = new MicroLibrary.MicroTimer();
Then I set the value:
microtimer1.Interval = 1;
And I added the method:
microtimer1.MicroTimerElapsed += new MicroLibrary.MicroTimer.MicroTimerElapsedEventHandler(microtimer1_Tick);
private void microtimer1_Tick(object sender, MicroLibrary.MicroTimerEventArgs timerEventArgs)
{ 
    label_time.Text = "Nerds do it better.";
    // more lines using Form1 (this) components will be here
}
 
And now I get this error:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
 
Additional information: Cross-thread operation not valid: Control 'label_time' accessed from a thread other than the thread it was created on.

What exactly did I do wrong? Thanks a lot!
Martina
AnswerRe: Cross-thread operation not valid [modified]memberken.loveday12-Apr-13 9:46 
Hi Martina.
 
The interval is in microseconds (not milliseconds), I expect you meant to write:
microtimer1.Interval = 1000; // 1ms
If you are updating a UI thread from another thread you will need to use a MethodInvoker:
private void microtimer1_Tick(object sender, MicroLibrary.MicroTimerEventArgs timerEventArgs)
{
    if (InvokeRequired)
    {
        BeginInvoke((MethodInvoker)delegate
        {
            label_time.Text = "Nerds do it better.";
        });
    }
}
I will try and update the article to include a win forms example. In the mean time I hope this helps.
 
Ken.

modified 13-Apr-13 11:16am.

AnswerRe: Cross-thread operation not validmemberken.loveday15-Apr-13 10:37 
Hi Martina,
 
I've now added a WinForms example to the project, I hope this helps!
 
Ken.
GeneralMy vote of 2memberjfriedman11-Apr-13 20:39 
Not sure what you were trying to accomplish here but you seem to have written a lot about it!
GeneralRe: My vote of 2memberDave Cross15-Apr-13 2:26 
And I have no idea why you think it only merits a 2.
 
Dave Cross
GeneralRe: My vote of 2memberYvar Birx15-Apr-13 10:34 
Perhaps because he has no idea what it is, and therefor gives it a two. Seriously, why would you rate something 5 stars if you have no idea what it is, or what it does.
GeneralRe: My vote of 2memberDave Cross15-Apr-13 22:18 
You don't have to give it any stars at all unless you choose to. If you deliberately rate it low, you deliberately flag it as poor quality. I happen to think that if you do that, common courtesy demands that you give an adequate explanation. Making snide remarks (because you have also written an article on the same topic) is just rude. It is also a dis-service to me as a bystander because I have no way to judge what the technical difference between the two approaches is.
 
This is a forum for sensible debate and advice, not for parading ego.
 
Dave Cross
GeneralRe: My vote of 2memberYvar Birx16-Apr-13 9:34 
Welcome to the Internet sir.
 
There is no such thing as logic here. Same goes with the random dislikes on YouTube videos.
QuestionObsoletememberjfriedman11-Apr-13 20:38 
Obtaining Microsecond Precision in .NET[^]
 
See my article for a much better alternative!
BugExcellent Implementation. Found a small bug and its work around for nowmemberwakaley24-Feb-13 19:43 
Hi , first of all I would like to thanks you alot. saved my lots of time. And really useful.
I found a small bug while using your micro timer.
It works perfectly fine when I am declaring the timer in the same method in which I am stopping it.
 
But if I declare it as member of class and 2 different methods are starting and stopping it then it has a small problem.
It starts well and run well too. Problem is with stopping. If I set
microtimer.Enabled == false; 
then it doesnt stop and hangs the application.
I also tried to use
microtimer.Stop();
it has the same issue.
WorkAround:
I used the
microtimer.Stop();
and commented out following:
while (Enabled)
{
      System.Threading.Thread.SpinWait(10);
}
 
And it solved the problem. I am not a pro. and I dont know what will be the side effects of it but for me its working well . I am using it to generate timer at 256 times in 1 second ( i.e. 256Hz)
GeneralRe: Excellent Implementation. Found a small bug and its work around for nowmemberken.loveday25-Feb-13 11:30 
Hi wakaley,
 
Thanks for the feedback and thanks for your vote, I'm glad the MicroTimer has proved useful.
 
If implemented correctly, declaring the MicroTimer as a class member and having separate start and stop methods should have no effect on the functionality of the timer. Depending on the implementation, removing the SpinWait while loop in the Stop method may be fine. However, if the timer is hanging in this loop then this means the timer thread (although maybe not doing any 'real' work) is still alive. Thus, removing the SpinWait while loop may be fine but in some cases may lead to undesirable behaviour.
 
Please note that the amount of work done in the OnTimedEvent must be very small. If the OnTimedEvent calls other methods then these should return immediately and should not keep any handles to the calling thread. If they do then it is entirely possible that the timer thread will always stay alive.
 
For the point of demonstration, if for example a UI is being updated, then:
 
This is fine:
private void OnTimedEvent(object sender,
                          MicroLibrary.MicroTimerEventArgs timerEventArgs)
{
    BeginInvoke((MethodInvoker)delegate
    {
        myWinformsLabel.Text = timerEventArgs.TimerCount.ToString();
    });
}
However this is not:
private void OnTimedEvent(object sender,
                          MicroLibrary.MicroTimerEventArgs timerEventArgs)
{
    Invoke((MethodInvoker)delegate
    {
        myWinformsLabel.Text = timerEventArgs.TimerCount.ToString();
    });
}
 
This is because Control.Invoke executes on the UI thread and the calling thread waits for completion before continuing (i.e. executes synchronously). Whereas Contol.BeginInvoke executes on the UI thread but the calling thread does not wait for completion before continuing (i.e. executes asynchronously).
 
Having said all of the above, it is certainly true that the current implementation of the MicroTimer Stop method may not be suitable for a number of situations. To this end I will look at revising the Stop method next time I do an update.
 
I hope this helps.
 
Ken.
GeneralMy vote of 5memberwakaley24-Feb-13 19:33 
Excellent implementation which I think should be part of the system itself. THanks a million
GeneralMy vote of 5memberdusty_dex21-Feb-13 7:39 
this article - perfect timing! (no pun intended)
GeneralRe: My vote of 5memberken.loveday21-Feb-13 10:49 
Ha, ha, Laugh | :laugh: Thanks for your vote and I hope you find the timer useful.
GeneralMy vote of 5memberEdo Tzumer20-Feb-13 20:52 
You earned this one Smile | :)
GeneralRe: My vote of 5memberken.loveday20-Feb-13 22:30 
Thanks!
GeneralMy vote of 5memberZaid Pirwani20-Feb-13 15:41 
if only I had found this last year... it took me some time to realize that the Timer control was not giving any TICK below 16ms approx.. THANKS
GeneralRe: My vote of 5memberken.loveday20-Feb-13 22:30 
Hi Zaid,
 
Thanks for your vote. I feel your pain on discovering the hard way the poor resolution offered by the Timer class. I appreciate it is a very hard problem to solve, but I hope one day there will be a main stream operating system (Windows 10 ?!) that will offer RTOS (Real Time Operating System) type behaviour i.e. deterministic timing operations around the 1ms mark.
 
Ken.
GeneralRe: My vote of 5memberZaid Pirwani21-Feb-13 2:52 
I doubt Windows BLUE, the next windows to come will have that or the one after it, but if MS is moving more towards the tablet and similar things then this might just become a reality some day....
BugLittle bugmemberD-Three7-Jan-13 3:57 
A while ago I needed something like this but couldn't find it so I started coding I my own version. Very rudimentary but workable but then I stumbled on your version! Funny that it is similar and yet so different Smile | :) And since your version offers everything I need and more, I ditched mine Wink | ;) So my vote of 5!
 
Found a little but though, if you create an instance of MicroTimer and then set Enabled to false or call the Stop() method, you get an exception since _threadTimer hasn't been initialized yet (only when the time has started).
 
So I changed this:
            if (_threadTimer.ManagedThreadId ==
                System.Threading.Thread.CurrentThread.ManagedThreadId)
            {
                return;
            }
to this:
 
            if (_threadTimer != null && _threadTimer.ManagedThreadId ==
                System.Threading.Thread.CurrentThread.ManagedThreadId)
            {
                return;
            }

GeneralRe: Little bugmemberken.loveday8-Jan-13 9:24 
Hi D-Three,
 
Firstly thanks very much for your vote, very pleased to hear the MicroTimer has been useful.
 
Secondly, thanks for pointing out the bug, bit of a 'school boy' error on my part! Next time I do an update I'll definitely include the fix.
 
Ken.
BugRe: Little bugmemberD-Three15-Jan-13 23:20 
No problem, it could happen to any programmer Smile | :) Glad I could contribute a tiny little bit.
 
Also, I noticed it has no effect if you change the Interval and IgnoreEventIfLateBy properties while the timer is running. I can see why but to be honest I don't need it. But System.Timers.Timer allows you to change the interval while running and since you made the comparison with System.Timers.Timer... Wink | ;)
 
Still love it, thanks! I will put it to test this week and see how it behaves on the long run (couple of weeks/months).
GeneralRe: Little bugmemberken.loveday16-Jan-13 3:01 
That's a very good point about changing the Interval and IgnoreEventIfLateBy while the timer is running, I'll definitely look at that when I next do an update. Thanks for the feedback - all very helpful.
GeneralRe: Little bugmemberken.loveday20-Feb-13 22:11 
Hi D-Three,
 
Following your (very helpful) feedback, 'Interval' and 'IgnoreEventIfLateBy' can now be changed whilst the timer is enabled. I've also fixed the unhandled exception error if Stop() is called before Start().
 
Thanks again, Ken.
GeneralRe: Little bugmemberD-Three1-Mar-13 4:28 
Thanks! Smile | :)
GeneralMy vote of 5memberMember 90455104-Dec-12 2:54 
Thank you guy!
 
It solved my problem in real time measurements with 200Hz.
GeneralRe: My vote of 5memberken.loveday4-Dec-12 8:54 
Glad it was helpful and thanks for your vote.
BugMicroStopwatchmemberdabalciunas7-Mar-12 3:20 
public long ElapsedMicroseconds
{
    get { return (long)(ElapsedTicks / _microSecPerTick); }
}
 
"ElapsedTicks" should be replaced by "Elapsed.Ticks":
 
public long ElapsedMicroseconds
{
    get { return (long)(Elapsed.Ticks / _microSecPerTick); }
}

GeneralRe: MicroStopwatchmemberken.loveday7-Mar-12 8:06 
Hi dabalciunas,
 
The MicroStopwatch class is correct as it is. The Elapsed property returns a TimeSpan, thus it would be possible to replace the ElapsedMicroseconds function with the code below (but not as you suggest).
public long ElapsedMicroseconds
{
    get { return (long)(Elapsed.TotalMilliseconds * 1000); }
}
I hope this helps.
 
Ken.
BugCPU LoadmemberMember 86133592-Feb-12 6:01 
Hi, i have test your code stopwatch for generated a quad wave using lpt port.
 
I've a big problem, when i enable your stopwatch, cpu load going to 100%. For test i've excluded the lpt port code and run only your code and after 1 secondo CPU 100%
 
I'm the only with this problem?
 
Thanks
GeneralRe: CPU Loadmemberken.loveday3-Feb-12 10:37 
Hi,
 
The only way to achieve such a high timer resolution (in the micro/millisecond range) is for the code to sit in a very tight while loop. The consequence of doing this is that a very heavy load is put on the CPU. Thus this is not a bug but simply a side effect of running the timer.
 
On a multi processor/core machine the operating system will allocate the timer process to one of the available processors and run that processor at near 100%. Thus, for example, on a machine containing 4 CPU's the system will effectively run at 25% CPU loading.
 
I hope this helps.
 
Ken.
GeneralRe: CPU LoadmemberMember 86133599-Feb-12 23:21 
For me it's not normal, i know one software can generate a quad signal of 100khx with a dual core cpu and 4%/5% cpu usage.... the only problem of yours timer is this :
 
you have missed a sleep... but i don't know how to do this because the minimum sleeis 1ms.... i think can it's possible to write a driver with c or assembler for generate a sleep of micro or nanosecondo.
 
What do you think about this?
GeneralRe: CPU Loadmemberken.loveday18-Feb-12 3:46 
Hi,
 
In practice the minimum sleep achievable is around the 15ms mark (even if you request 1ms). Instead of a sleep the code 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. It is the only solution available in this type of situation and is an optimisation technique used in very tight loops such as a 'lock'.
 
If there was some hardware to interface with, it may be possible to generate a sleep but once the sleep ends the code would still be at the mercy of the operating system. In addition any hardware driver would be specific to the hardware on that machine.
 
The fundamental problem is that a conventional operating system (such as Windows) is not able to perform deterministic timing operations. MicroTimer does not and cannot solve this problem, the only way to achieve deterministic timing behaviour is with an RTOS (Real Time Operating System) like IntervalZero RTX:
 
http://www.directinsight.co.uk/products/venturcom/rtx.html
http://www.intervalzero.com/products/rtx-at-a-glance/
 
Ken.
GeneralRe: CPU Loadmembernandkishor209e29-Mar-12 2:09 
HI ...
use GPU processing for sol this problem
Nvidia Cuda Toolkit
GeneralMy vote of 5memberChong Andy27-Dec-11 19:52 
This is the great job,and I will try this code to control my hardware to enable for 1ms scanning rate.
 
i have one question, does this code available for c ?
GeneralRe: My vote of 5memberken.loveday28-Dec-11 1:39 
Hi Chong Andy,
 
Thanks for your vote.
 
I've not done so but it should be possible to produce similar code for Windows in C++.
 
QueryPerformanceCounter() and QueryPerformanceFrequency() can be used to produce a high resolution stopwatch timer. The code snippets below show how to use these functions:

http://www.mindcontrol.org/~hplus/misc/simple-timer.html
 
http://stackoverflow.com/questions/1739259/how-to-use-queryperformancecounter
 
SetPriorityClass() and SetThreadPriority() can then be used to increase the timer thread's scheduling priority:
 
http://msdn.microsoft.com/en-us/library/ms685100.aspx
 
I hope this helps.
 
Ken.
GeneralRe: My vote of 5memberChong Andy30-Dec-11 3:07 
Thanks for the suggestion and information.
 
I will feed back to you after I test your code in my application. ^^ Laugh | :laugh:
GeneralMy vote of 5memberKanasz Robert29-Nov-11 22:51 
Good article Smile | :) It is very interesting for me.
GeneralRe: My vote of 5memberken.loveday29-Nov-11 22:59 
Thanks for your vote!
GeneralMy vote of 5memberChris KMPP26-Nov-11 5:36 
Nice article.
GeneralMy vote of 5memberPrafulla Hunde24-Nov-11 17:04 
Very useful information.
Questionis it unstable?memberAuspexPT15-Nov-11 6:01 
Hi Ken,
 
I was testing your Timer class but i've noticed that it's not very stable under my scenario.
Maybe i'm doing something wrong, but i dont think so.
my code is moving the mouse from point A to point B automatically and i use the timer to do a smooth transition.
The thing is that i only want the timer to stop when the mouse reaches the destination point. So i have to put the
microTimer.Enabled = false;
inside the "Tick" function. However it seems that if i click the start button two times, the first one it works ok but the second one it starts to hang my pc. My guess is that it's not disabling the timer, making it enter on an infinite loop.
Can you tell me what's wrong?
Thanks
 
    public partial class FormMain : Form
    {
        //mouse stuff
        private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
        private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;
        [DllImport("user32.dll")]
        private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, uint dwExtraInf);
        
        //program vars
        Point startPoint = new Point(980, 520);
        Point currentPoint = new Point(0, 0);
        Point destinationPoint = new Point(1055, 650);
 
        MicroTimer microTimer = new MicroTimer();
 
        public FormMain()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            currentPoint = startPoint;
            Cursor.Position = currentPoint;
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);//make left button down
            
            microTimer = new MicroTimer();
            microTimer.MicroTimerElapsed += new MicroTimer.MicroTimerElapsedEventHandler(OnTimedEvent);
            // Call micro timer every 1000 µs (1ms)
            microTimer.Interval = 1000;
            //start timer
            microTimer.Enabled = true;
 
        }
        private void OnTimedEvent(object sender, MicroTimerEventArgs timerEventArgs)
        {
            if (currentPoint.X!=destinationPoint.X)            
                currentPoint.X++;
            if (currentPoint.Y!=destinationPoint.Y)
                currentPoint.Y++;
 
            Cursor.Position = currentPoint;
            if (Cursor.Position == destinationPoint) //condition to stop the timer
            {
                mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);//make left button up
                microTimer.Enabled = false;
            }
        }
    }

AnswerRe: is it unstable?memberken.loveday15-Nov-11 12:25 
Hi AuspexPT,
 
You are correct, the timer does enter an infinite loop. The code below should fix your problem:
if (Cursor.Position == destinationPoint) //condition to stop the timer
{
    mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);//make left button up
    new System.Threading.Thread(delegate(){ microTimer.Enabled = false; }).Start();
}
I hope this helps.
Ken.
GeneralRe: is it unstable?memberAuspexPT19-Nov-11 9:13 
Yes, it works... Thanks for the help Smile | :)
 
BTW, why do we need the delegate in this case and not on your example?
GeneralRe: is it unstable?memberken.loveday19-Nov-11 11:38 
Hi AuspexPT,
 
Glad to help.
 
In answer to your question, the other solution would be to remove the while(Enabled){} loop in the MicroTimer Stop() function. When I originally wrote the article I was wondering whether to include the while loop for exactly this situation (I apologise, as on reflection I should have at least added a note in the article indicating this possible scenario). To explain, the while loop waits until the current OnTimedEvent finishes, of cause if the stop (enabled=false) is put inside the OnTimedEvent then the timer could enter an infinite loop (as you discovered). In your implementation removing the while loop in the stop function will be fine but in other implementations this could lead to some undesirable behaviour.
 
When I get some time I will update the article/code and put some code in to cater for your situation. In the mean time happy coding and thanks for your question.
 
Ken.
GeneralRe: is it unstable?memberken.loveday28-Nov-11 9:04 
Hi AuspexPT,
 
Thought you would like to know that I've updated the project. The MicroLibray should now work with your original code.
 
Ken.
GeneralWaveform From OSC ! [modified]memberShem Su3-Aug-10 20:21 
hi Ken,
 
I've done the test, the interval of timer is 1μs, and the cycle of output waveform is about 10μs - 11μs.The following Images were captured from the OSC,
Image1

Image2

Image3

(How to upload images in the reply??Confused | :confused: )
 
The cycle of the waveform was not stable, i guess it's caused by the OS and we have no way to fix it, what's your opinion Ken?
 
My code is here,
        MicroTimer timer;
        bool Voltage = false;
 
        public Form1()
        {
            InitializeComponent();
            timer = new MicroTimer();
            timer.MicroTimerElapsed += new MicroTimer.MicroTimerElapsedEventHandler(timer_MicroTimerElapsed);
            timer.Interval = 1;
            timer.Start();
        }
 
        void timer_MicroTimerElapsed(object sender, MicroTimerEventArgs timerEventArgs)
        {
            int data = 0x0;
            Voltage = !Voltage;
            data ^= Convert.ToInt16(Voltage);
            PortAccess.Output(PortAccess.LTP1_REG_DATA, data);
        }
     

modified on Wednesday, August 4, 2010 2:28 AM

GeneralRe: Waveform From OSC !memberken.loveday3-Aug-10 22:32 
Hi Shem,
 
Thanks very much for this, this is a really interesting test. Clearly the maximum turnaround time (on your system) is 10μs, it would be interesting to run the test on a faster and/or slower PC and see how the results change. Either way it's difficult to know where the bottle neck / limit is (i.e. software, hardware, OS, etc). For the same reason it's hard to know why the waveform is unstable, however I agree with you that the instability is likely to be caused by the OS (and as such cannot be 'fixed'). To be honest I would not expect it to be that stable at such a small intervals.
 
If it's easy for you to do, I would be very interested to see the same test results with the interval set at 20μs, 100μs, 1000μs
 
Thanks again for taking the time to do the tests – really good work.
 
Ken Johnson, I would be interested in your comments.
 
Ken.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130617.1 | Last Updated 14 Apr 2013
Article Copyright 2010 by ken.loveday
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid