Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronizing Forms.Timer and Diagnostics.Stopwatch

I have a function (say foo())that will be called from time to time with a variable interval. When it is called, it checks the time and takes an action accordingly.

I have done this in the following way:

  • A Forms.Timer object invokes the function when required

  • A Diagnostics.Stopwatch object is used within the function for the purpose of determining the time and deciding what to do.

However I have the following problem: when foo() is called by Timer's callback, the ElapsedMilliseconds value of stopwatch object is usually lower than expected. For example, timer is set to 1000 so after 1000 ms foo() is called, but within foo() body ElapsedMilliseconds return 900 therefore foo behaves as if the elapsed time was 900 (although it should take the action A because 1000 ms actually elapsed, it does not)

How can I synchronize timer and stopwatch in such case that ElapsedMilliseconds have a consistent value with timer?

EDIT: Some Code

Some sample code to explain what is my problem:

//foo is the function that is called by timer's callback
public void foo()
{
    //Let's see what time it is: 
    long currentTime = stopwatch.ElapsedMilliseconds();
    Item = getCurrentItem(currentTime);
    Item.Draw();
}

//this is the callback of timer
private void timer1_Tick(object sender, EventArgs e)
    {
        //set the timer for next time
        timer1.Interval = Intervals[periodIdx++];
        foo();
    }

This is supposed to draw something else each time when an interval is completed, however since ElapsedMilliseconds return an earlier value than timer claims, although the interval is over, next item isn't drawn

like image 876
Hayri Uğur Koltuk Avatar asked Feb 04 '26 08:02

Hayri Uğur Koltuk


2 Answers

You get the big difference because you start the timer somewhere within the 1/64 second interval. You'll get better results with this:

    private void StartTimers() {
        int tick = Environment.TickCount;
        while (Environment.TickCount == tick) Thread.Sleep(0);
        timer1.Enabled = true;
        stopwatch.Start();
    }

Where the while() loop improves the odds that the timer gets started at the start of a 1/64 timer tick. Just improves, no guarantees. And you can't do anything about the Tick event firing late, it entirely depends on the responsiveness of your UI thread. It is however always late. Don't use this code, write your code so you don't care that these timers are not in sync. You may have to reduce the timer's Interval to accomplish that, it isn't clear from the question.

like image 101
Hans Passant Avatar answered Feb 05 '26 21:02

Hans Passant


You aren't going to have much success with this approach. You're not starting each timer at the exact same time and you're not checking them at the exact same time (there some passage of time between the Timer firing it's event and your code querying the Stopwatch).

Pick a single timer and base everything off of it if you want things in sync. For example, if you want to go with the Forms.Timer, in your event handler for it just increment a counter variable - that will tell you how many times your handler has been called and, effectively, how much time the Forms.Timer says has passed. Here's an example (I'll leave it to you to handle the case of the timer ticking long enough that the counter exceeds long.MaxValue)

public void foo()
{
    Item = getCurrentItem(totalElapsed);
    Item.Draw();
}

long totalElapsed = 0;

private void timer1_Tick(object sender, EventArgs e)
    {
        totalElapsed += timer1.Interval;
        //set the timer for next time
        timer1.Interval = Intervals[periodIdx++];
        foo();
    }
like image 38
Robert Levy Avatar answered Feb 05 '26 23:02

Robert Levy