Thursday, December 4, 2014

System.Threading.Timer is firing before its supposed to

Problem
I'm using a System.Threading.Timer and specifying a precise time of 10:05 pm, but the timer fires (callback is called) before that time, such as 10:04:59.97 pm.

This causes problems for me because I have to recalculate the next time it should fire based on the current time, and the current time is before the time it was supposed to fire the first time.

Solution
Timers aren't guaranteed to fire exactly when you schedule them to. They will fire a little before or after the time you scheduled. For my situation it's bad when they fire before they're supposed to, because subsequent scheduling is affected by this. Therefore if the timer fires before it's supposed to i sleep the thread until its at or after its expected time. This is achieved by keeping track of the Expected Time in the callback state object.

Code
Here's a stripped down version of my code that's used to handle this situation

        internal class TimerStateObject
        {
            internal System.Threading.Timer refTimer = null;
            internal DateTime expectedTime;

        }

Here's the timer init and callback functions

        private void initTimer()
       {
            TimerStateObject timerStateObject = new TimerStateObject();
         
             timerStateObject.expectedTime = DateTime.Now.Add(ONE_HOUR);
         
             timerStateObject.refTimer = new System.Threading.Timer(new TimerCallback(timerCallBack), timerStateObject, ONE_HOUR , DISABLE_PERIOD);
       }

        private void timerCallBack(Object timerStateObject)
        {
            TimerStateObject state = (TimerStateObject)timerStateObject;
         
            waitUntilExpectedTime(state);

            state.targetTime = DateTime.Now.Add(ONE_HOUR);

            state.refTimer.Change(ONE_HOUR, DISABLE_PERIOD);
        }


        private void waitUntilExpectedTime(TimerStateObject timerStateObj)
        {
            DateTime now = DateTime.Now;
            while (now.CompareTo(timerStateObj.expectedTime) <= 0)
            {
                TimeSpan diff = timerStateObj.expectedTime.Subtract(now).Add(ONE_SECOND));
                Thread.Sleep(diff);
                now = DateTime.Now;
            }
        }

No comments:

Post a Comment

There was an error in this gadget