views:

1103

answers:

3

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);
+1  A: 

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?

Drew Marsh
funny, you should mention that. That's exactly where I started, in fact my first question had exactly that so I'll add it back in.
discorax
Thanks for adding that detail. So you're pretty much just changing the value of the Angles, I doubt very highly this is causing a problem either. Can you change the timer so that it fires ever 10th or 100th of a second? If it really has anything to do with this timer loop that would help accelerate the process so it's much easier to reproduce.
Drew Marsh
That's a good suggestion. I did that and it seems to accelerate the problem. There is one other detail I'm creating an instance of the Clock class each time, that's the constructor I refer to. It's possible that the Clock object is not being collected by GC properly which may be the culprit.
discorax
+2  A: 

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.

RandomEngy
I've rebuilt the Clock class to include a DisposeTimer method which I call right before I remove the Clock instance from the Visual Tree. I'm running a test right now to see if that solves the issue. I'm keeping my fingers crossed.
discorax
a reworking of the Clock class seems to have fixed some of the issues. Since I put a bounty on this question I had to accept an answer, however, the DispatcherTimer issue is still outstanding. I hoped that someone with experience working with multiple threads would chime in, but was not to be.Thanks for your help.
discorax
What leads you to believe it's an issue with the DispatcherTimer itself, rather than the number of them you have around? Did you check how often your Clock_Tick method is being called? The DispatcherTimer is pretty solid: at regular intervals, it just puts a method on the queue for the UI thread to invoke. If your app is locking up due to excessive CPU use on the UI and render threads, it really isn't a multi-threading problem anymore, it's more a question of "why I am invoking so many calls to the UI thread?"
RandomEngy
+1  A: 
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.

Akash Kava
I agree the performance would be helped by not doing all of the work on the dispatcher thread, but it doesn't seem to directly address the problems of the orginal post. Longer term this may be a good idea, but I suspect there is actually more overhead in changing the angles than the date math that's going on above, so it might be a little bit of an over-optimization.
Drew Marsh
You can profile and find out queue length of dispatcher object some how, but PriorityQueue of dispatcher object is private member so i dont know how to see it. But I am sure, longer terms may be better idea because you are certainly pushing more into dispatcher queue then it can handle. I have worked in depth with dispatcher threads, sometimes even 2-3 mill seconds of code on different thread does make little difference, you wont notice until it accumalates for hours.
Akash Kava
The amount of work in this example being done in the Dispatcher method is absolutely trivial. I mocked up a similar program and had the event firing every millisecond and the program stayed below 1% average CPU use. I agree with Drew: focusing on the CPU use of the tick method is premature optimization.
RandomEngy