Suppose I have a WPF application where I have the following architecture:
[Worker Thread] -> [Queue 1] -> [Queue Manager Thread] -> [Queue 2] -> [UI Thread]
The Worker is listening to data from some service and receives that data at indeterminate times (could be multiple times per second or a few times every few seconds) then queues it to Queue 1, then the Queue Manager, depending on the "health" of the UI thread may decide to throttle up / down the rate that data items are queued to Queue 2, which the UI thread uses to update the UI, maybe dropping a few items, so as not to overwhelm the UI thread in the event it is receiving too many messages (eg it might decide that it will check the timestamp of each data message and only enqueue it to Queue 2 if the difference between messages is at least 5 seconds older than the last data item the UI updated itself with)
The UI thread will have a timer that fires a set-interval to update the UI with new data from Queue 2. What I'd like to be able to do is determine how fast the UI is updating itself to measure it's "responsiveness" in order to throttle eg increase / decrease the timer-interval as to how often to update the UI
Suppose my UI had lots of controls (grids, charts etc) all bound to different filtered / grouped subsets of the data on Queue 2 in my UI shell and the UI starts becoming unresponsive and freeze between updates when updating those controls, how could I detect this from code in order to know how / when to increase / decrease the interval for UI updates? Basically, how do I measure how long it takes to rebind the entire UI across all controls bound to the data?
BTW is this a good design or could it be improved? Are there any other strategies I could consider?
There are 2 threads...the main UI thread....and the rendering thread....you might need to look at both to determine the responsiveness of your application...and decide how to throttle.
You could watch ETW events, or handle CompositionTarget.Rendering and count the frames yourself....this is monitoring the rendering thread...if that's dropping frames, then it could tell you that your system is overburdened...and your could throttle your background work accordingly.
http://blogs.msdn.com/b/jgoldb/archive/2008/09/26/etw-event-tracing-in-wpf.aspx
Why is Frame Rate in WPF Irregular and Not Limited To Monitor Refresh?
You could use the DispatcherTimer as mentioned by @HenkHolterman to monitor the load on the UI thread by getting it to be run at a priority of below Normal e.g. Background....when the timer event is handled/processed on the UI thread....you can then release/tell your background worker to do the next item of work.
But be careful...if your timer interval is too small...and your system is overloaded...then you may get a build up of timer messages...and so when your eventhandler is called...you might do too much work (unless you kept a record of the last time it was called).
Alternatively you could just get a delegate to be run on the UI thread at a particular priority (e.g. usually Background).
If you use Dispatcher.BeginInvoke....then the work is put into a queue, and will be executed when all higher-priority work has completed. (asynchronous).
If you use Dispatcher.Invoke...then your thread will block, until all higher priority work in the Dispatcher you are Invoking to has completed, then your delegate will execute.
(your Background worker would do this when it had finished its unit of work, and the delegate would then tell your Background worker to do the next unit of work).
This might be better than a DispatchTimer as you can remove some latency...i.e. using a timer would have a latency dependent on the timer interval.
Then depending on what you find, you can adjust the next unit of work.
If you really want to get sophisticated then you could monitor various Performance Counters from inside your application e.g. use of memory, GC collections, etc...and dynamically throttle the amount of work you do.
http://weblogs.asp.net/pawanmishra/archive/2010/06/06/understanding-dispatcher-in-wpf.aspx
http://msdn.microsoft.com/en-us/magazine/cc163328.aspx
I would simply drop the QueueManager and Queue2.
All you need is a WPF Dispatcher Timer (and not a Timers.Timer) that checks for input every few ms. You can statically configure that timeout.
And then make Queue1 a blocking queue to throttle whatever is upstream.
The Dispatcher Timer can be started with a certain priority. Pick a low one, like Background or ContextIdle and you will never overload the GUI.
Then just make sure the Timer doesn't bite off more than it can chew. Only 1 (or a few) items at a time. Tune this part so that the GUI will process as much as it can but no more. The runtime adjustment comes for free because you're tied into the dispatcher.
And (only when needed) you can use Queue1 = new BlockingCollection<MyItemType>(MaxItems)
so that this queue doesn't overfill.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With