Adding many entries to an Observable collection in a performance friendly way

I was writing a WPF application which was polling a lot of web services in order to display stuff in a list. The items change infrequently but required constant polling in order to make sure that they stayed the same. The problem appeared when they suddenly changed a lot. You see, the built-in Observable collection does not like when you toy around with it a lot and will send tons of events. So, while my app mostly chugged along with a few new entries an hour, sometimes it got a thousand in one fell swoop – causing unresponsiveness.

I googled around a bit, and found this
article
, that while good did not help my situation. Since I cannot know when I’ll receive many entries, and I’d like to have a simple interface I have improved on this solution a bit.

Behold, the DeferEventObservableCollection:

public class DeferEventObservableCollection : ObservableCollection
{
	private readonly List<NotifyCollectionChangedEventArgs> deferredEvents = new List<NotifyCollectionChangedEventArgs>();
	private bool hasQueuedDispatcherUpdate = false;
	private readonly int threshold;
	private object syncRoot = new object();

	public DeferEventObservableCollection(int threshold = 10)
	{
		this.threshold = threshold;
	}
	
	protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
	{
		lock (syncRoot)
		{
			deferredEvents.Add(e);
			if (!hasQueuedDispatcherUpdate)
			{
				Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
				{
					lock (syncRoot)
					{
						if (deferredEvents.Count > treshold)
						{
							base.OnCollectionChanged(
								new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
						}
						else
						{
							foreach (var ev in deferredEvents)
							{
								base.OnCollectionChanged(ev);
							}
						}
						deferredEvents.Clear();
						hasQueuedDispatcherUpdate = false;
					}
				}));
			}
			hasQueuedDispatcherUpdate = true;
		}
	}
}

What I have made is a collection that will seamlessly handle the issue of suddenly getting a lot of changes in a more graceful way. Adding an item or removing it will catch the event that the observable collection normally fires and queue it up on the dispatcher queue. Since you normally get your items added in the dispatcher thread this event will then be deferred to later on in the same chunk of dispatcher work (or the next chunk). Events keep piling up in this queue and once it’s time to fire the events the collection will evaluate the size of the event list. If it is above some threshold value it will replace the events in the queue with a single Reset event. You’ll need to experiment with the threshold to find the optimum number, the 10 is a raw guess, and it will vary on how many listeners you have on your collection.

Being the concurrency-aware programmer that I am, I have added some extra measure of thread safety so that it cannot go haywire. When emptying the event queue, if another thread should attempt to add a new even to the queue, the lock will prevent this from blowing up in your face.

This simple drop-in replacement changed the performance on my application by several orders of magnitude, and I hope you can find it as useful as I did in your project!

Edit: I fixed the locking, it no longer locks on this which is bad form. Instead it locks on a syncroot as it should. Thanks @pheiberg for pointing it out!

Advertisements
Tagged , , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: