discorax
discorax

Reputation: 1487

DispatcherTimer eating up CPU over time causing WPF visual not rendering properly

I have a WPF app that uses DispatcherTimer to update a clock tick.

However, after my application has been running for approx 6 hours the clocks hands angles no longer change. I have verified that the DispatcherTimer is still firing with Debug and that the angle values are still updating, however the screen render does not reflect the change.

I have also verified using WPFPerf tools Visual Profiler that the Unlabeled Time, Tick (Time Manager) and AnimatedRenderMessageHandler(Media Content) are all gradually growing until they are consuming nearly 80% of the CPU, however Memory is running stable.

The hHandRT.Angle is a reference to a RotateTransform

hHandRT = new RotateTransform(_hAngle);

This code works perfectly for approx 5 hours of straight running but after that it delays and the angle change does not render to the screen. Any suggestions for how to troubleshoot this problem or any possible solutions you may know of.

.NET 3.5, Windows Vista SP1 or Windows XP SP3 (both show the same behavior)

EDIT: Adding Clock Tick Function

//In Constructor
...
_dt = new DispatcherTimer();
_dt.Interval = new TimeSpan(0, 0, 1);
_dt.Tick += new EventHandler(Clock_Tick);
...

 private void Clock_Tick(object sender, EventArgs e)
        {

            DateTime startTime = DateTime.UtcNow;
            TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById(_timeZoneId);
            _now = TimeZoneInfo.ConvertTime(startTime, TimeZoneInfo.Utc, tst);
            int hoursInMinutes = _now.Hour * 60 + _now.Minute;
            int minutesInSeconds = _now.Minute * 60 + _now.Second;
            _hAngle = (double)hoursInMinutes * 360 / 720;
            _mAngle = (double)minutesInSeconds * 360 / 3600;
            _sAngle = (double)_now.Second * 360 / 60;
            // Use _sAngle to showcase more movement during Testing.
            //hHandRT.Angle = _sAngle;
            hHandRT.Angle = _hAngle;
            mHandRT.Angle = _mAngle;
            sHandRT.Angle = _sAngle;

            //DSEffect
            // Add Shadows to Hands creating a UNIFORM light
            //hands.Effect = textDropShadow;
        }

Along the lines of too much happening in the clock tick, I'm currently trying this adjustment to see if it helps. Too bad it takes 5 hours for the bug to manifest itself :(

  //DateTime startTime = DateTime.UtcNow;
  //TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById(_timeZoneId);
  //_now = TimeZoneInfo.ConvertTime(startTime, TimeZoneInfo.Utc, tst);
  _now = _now.AddSeconds(1);

Upvotes: 5

Views: 7376

Answers (3)

Akash Kava
Akash Kava

Reputation: 39956

hHandRT.Angle = _hAngle;
mHandRT.Angle = _mAngle;
sHandRT.Angle = _sAngle;

I believe you have to look at your above code once again.

You are setting Angle property of your transform for all 3 transforms even if you dont need them to change every second. Your minute will change for every 60 changes and your hour will change for every 3600 seconds. However you can atleast reduce your changing hours angle for every second.

What is happening here is, whenever you request transform changes to WPF, WPF queues the request to priority dispatch queue and every second you are pushing more changes to be done then it can process. And this is the only reason your CPU usage keeps on increasing instead of memory.

Detailed Analysis:

After looking at your code, I feel your DispatcherTimer_Tick event does too much of calculation, remember Dispatcher thread is already overloaded with lots of things to do like managing event routing, visual update etc, if keep your cpu more busy to do custom task in dispatcher thread that too in every second event, it will definately keep on increasing the queue of pending tasks.

You might think its a small multiplication calculation but for Dispatcher thread it can be costly when it comes to loading timezones, converting time value etc. You should profile and see the tick execution time.

You should use System.Threading.Timer object, that will run on another thread, after every tick event, when you are done with your calculations of final angles required, you can then pass them on to Dispatcher thread.

like,

Dispatcher.BeginInvoke((Action)delegate(){
   hHandRT.Angle = _hAngle;
   mHandRT.Angle = _mAngle;
   sHandRT.Angle = _sAngle;   
});

By doing this, you will be reducing workload from dispatcher thread little bit.

Upvotes: 1

RandomEngy
RandomEngy

Reputation: 15413

You say you're creating an instance of the Clock class each time? Note that timers in .NET will root themselves to keep themselves from being garbage collected. They'll keep on firing until you stop them yourself, and they will keep your Clock objects alive because they are referenced in the timer tick event.

I think what's happening is that with each Clock you create you start another timer. At first you only fire 1 event per second, but then you get add on another timer and get 2 per second, and they continue to accumulate in this way. Eventually you see your Tick handler and AnimatedRenderMessageHandler rising in CPU until they bog down and are unable to update your screen. That would also explain why increasing the frequency of the timer firings made your symptoms appear sooner.

The fix should be simple: just stop or dispose the DispatcherTimer when you are done with your Clock object.

Upvotes: 5

Drew Marsh
Drew Marsh

Reputation: 33379

You're assuming it's the DispatcherTimer and focusing totally on that. I personally have a hard time believing it has anything to do with the timer itself, but rather think it has to do with whatever you're doing within the timer tick. Can you tell us more about exactly what is going on each time the timer ticks?

Upvotes: 1

Related Questions