0

I'm having a weird problem with timers. As long as I know, the interval property of a timer indicates the delay between loops in which the timer_Tick event will be fired.

I had this problem with the exact signature (15 and 16 milliseconds delay) before while programming in Visual Basic. Any timers that I create, fire their tick event with 15 or 16 milliseconds delay. For instance, if I set the interval of my timer to 1 (which means its tick event should get fired 1000 times in 1 second), the event instead gets fired 62 to 66 times in 1 second (that's 1000/16 to 1000/15).

I've been developing VB applications since 5 years ago and always had this problem (that also means I had this problem on several different systems with both AMD and Intel processors), and now I'm having it again with C#.

I'd managed to do a workaround and solve this problem by calculating the time difference between each time the tick event gets fired based on TickCount method (GetTickCount API in VB and Environment.TickCount in C#).

*TickCount is the amount of milliseconds passed since the time system had started.

To understand the problem better, I created a windows application that counts the seconds since it gets executed (like a timer). It relies on both TickCount and ordinary addition on each time the Tick event gets fired. It also calculates the delay of the timer by subtracting the last value of TickCount from the current value of the TickCount (if the timer would have been fired 1000 times in 1 second truly, then the difference of the TickCounts would be 1 each time, thus meaning there's no delay, but if the difference is more than 1, then there is some delay between each times the timer's tick event gets fired).

Here's the code:

public partial class Form1 : Form
{
    int localTime = 0, systemTime = 0, baseSystemTime = 0, lastSystemTime = 0;
    public Form1()
    {
        InitializeComponent();
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        // Calculate time based on TickCount
        if (baseSystemTime == 0)
            baseSystemTime = Environment.TickCount;

        systemTime = Environment.TickCount - baseSystemTime;
        label2.Text ="System Time: " + ((systemTime / 1000) / 60).ToString() + ":" + ((systemTime / 1000) % 60).ToString();

        // Calculate time based on timer1_Tick
        localTime++;
        label1.Text = "Application Time: " + ((localTime / 1000) / 60).ToString() + ":" + ((localTime / 1000) % 60).ToString();

        // Calculate the delay
        if (lastSystemTime > 0)
        {
            label3.Text = "Delay: " + (Environment.TickCount - lastSystemTime).ToString() + " ms";
        }

        lastSystemTime = Environment.TickCount;
    }
}

I've also uploaded the whole solution here: http://ramt.in/test/TimerDelay.zip

Here's a screenshot of the application (with 15 milliseconds delay and 1 second counted by application while 17 seconds have actually passed!):

Screen Shot

The solution is only 50kb, so feel free to download it and run it to see if you get the same result as me. If it's the same, then there's something wrong with the timer class in Microsoft world!

But more importantly, if anyone knows anything about what might cause this delay, please share your knowledge with me.

Ramtin Soltani
  • 2,650
  • 3
  • 24
  • 45
  • possible duplicate of [timer is not working well with TimeSpan from milliseconds](http://stackoverflow.com/questions/26703794/timer-is-not-working-well-with-timespan-from-milliseconds) – Jordi Vermeulen May 02 '15 at 06:45
  • Windows is not real time so this will happen. Have a look at this http://ideveloper-dotnet.blogspot.com/2013/07/real-time-timer-in-c.html – Jibran Khan May 02 '15 at 06:46

2 Answers2

2

This has nothing to do with realtime or not: The windows default timer resolution is 64 ticks/s or 15.625 ms. However, the systems timer resolution may be modified to operate at higher resolution, e.g. 1 ms. See this aswer to the question "Why are .NET timers limited to 15 ms resolution?" to get an idea about how to modifiy the systems timer resolution.

Community
  • 1
  • 1
Arno
  • 4,994
  • 3
  • 39
  • 63
  • Are there any better ways than changing the timer resolution? Would using System.Timers.Timer instead of System.Windows.Forms.Timer (the default timer in VS toolbox) result in more accuracy? – Ramtin Soltani May 02 '15 at 12:13
  • Unfortunately not. However you may look at [how to set timer resolution from C# to 1 ms?](http://stackoverflow.com/q/15071359/1504523) to see c# examples. But you should also [Why does increasing timer resolution via timeBeginPeriod impact power consumption?](http://stackoverflow.com/q/7590475/1504523) in mind. – Arno May 02 '15 at 15:23
1

This is a system problem not c# or VB. To check how accurate is your system toy can use Stopwatch class and two properties

  1. IsHighResolution - The timer used by the Stopwatch class depends on the system hardware and operating system. IsHighResolution is true if the Stopwatch timer is based on a high-resolution performance counter. Otherwise, IsHighResolution is false, which indicates that the Stopwatch timer is based on the system timer.
  2. Frequency

The following code from MSDN shows how it works

public static void DisplayTimerProperties()
{
    // Display the timer frequency and resolution. 
    if (Stopwatch.IsHighResolution)
    {
        Console.WriteLine("Operations timed using the system's high-resolution performance counter.");
    }
    else 
    {
        Console.WriteLine("Operations timed using the DateTime class.");
    }

    long frequency = Stopwatch.Frequency;
    Console.WriteLine("  Timer frequency in ticks per second = {0}",
        frequency);
    long nanosecPerTick = (1000L*1000L*1000L) / frequency;
    Console.WriteLine("  Timer is accurate within {0} nanoseconds", 
        nanosecPerTick);
}

UPDATE

You also did an error in your code:

        // Calculate time based on timer1_Tick
        localTime++;
        label1.Text = "Application Time: " + ((localTime / 1000) / 60).ToString() + ":" + ((localTime / 1000) % 60).ToString();

This lines has nothing to time which passed. It only calculates how many times timer1_Tick was run. The 1 millisecond interval is just too small for windows forms timer. You can read about this here: Timer takes 10 ms more than interval

If you need more precise timer you could look at this article Microsecond and Millisecond C# Timer

Community
  • 1
  • 1
Piotr Stapp
  • 19,392
  • 11
  • 68
  • 116
  • Did it and got these results: IsHighResolution returns true, frequency is 2338336 and the accuracy is 427 nanoseconds. What does it mean? – Ramtin Soltani May 02 '15 at 12:55
  • I looked at the article (Microsecond Timer) and it is what I'm looking for, but, there's only one problem and that's cross-thread issue. How can I solve it? Is there a property equivalent to SynchronizingObject? – Ramtin Soltani May 03 '15 at 03:34
  • @RamtinSoltani MicroTimerElapsedEventHandler is a delegate which you can handle in your threat – Piotr Stapp May 03 '15 at 18:05
  • Let me make my question more specific, I assigned a function to the event handler (using the delegate), then in that function I tend to change properties of a control object in my form. That's where I get compiler error that says I can't access the object from another thread. – Ramtin Soltani May 04 '15 at 00:21
  • It is a common error. Look here: http://stackoverflow.com/questions/9732709/the-calling-thread-cannot-access-this-object-because-a-different-thread-owns-it – Piotr Stapp May 04 '15 at 07:20
  • I think the solution you provided only works for WPF and not WinForms, my compiler says there's no method called Dispatcher: 'Cointhusiast.Form1' does not contain a definition for 'Dispatcher' and no extension method 'Dispatcher' accepting a first argument of type 'Cointhusiast.Form1' could be found (are you missing a using directive or an assembly reference?). What can I do now? – Ramtin Soltani May 04 '15 at 08:09
  • Just look on StackOveflow: http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c – Piotr Stapp May 04 '15 at 10:50
  • Thank you Garath, you've been a great help. Accepted. – Ramtin Soltani May 06 '15 at 01:30