views:

289

answers:

3

In a multi-threaded WPF application, it is not possible to update an ObservableCollection from a thread other than WPF window thread.

I know there are workarounds, so my question is not how to avoid the "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread" exception.

My question is, why there is such an exception? Why wasn't it possible to allow collection updates from any thread?

Personally, I don't see any reason to block UI update when ObservableCollection is changed from other threads. If two threads (including parallel ones) are accessing the same object, one listening for changes of object properties through events, the other one doing changes, it will always work, at least if locks are used properly. So, what are the reasons?

+5  A: 

If your collection is bound to user interface elements, those user interface elements are listening on the CollectionChanged event of the collection, and this event is raised on the thread, on which you are updating the collection.

So the problem is with the user interface elements, which can only be accessed from the thread, on which they were created, and not with the collection itself.

treaschf
A: 

But "WPF [...] supports property changes across threads", so where is the problem?

The CollectionChanged event will be raised both if the collection is modified from the same thread or from any other thread.

So I really don't see what is the problem for WPF to update elements according to the changes made from objects from other threads (except the obvious multi-threading issues like accessing and changing an object at the same time, but those can be solved with obvious solutions).

MainMa
You cannot modify a UI control from any thread other than the UI thread..ever..not even a little. In the post you linked "..marshal change notifications to a Dispatcher's thread." is the important part. The changes always take place on the UI thread....might look like it..but that IS the way it works.
Rusty
+2  A: 

First...I feel your pain. The Ui thread restriction can be a pain...

Why can't you update a Ui Element from a thread other than the one it was created on ?

My question is, why there is such an exception?

Well in a nutshell, history. Windows has been around a while and the way some parts of the Gui work are embedded in technologies such as COM and the like....so changing it is not trivial...would be very easy to break something. There are many other issues I'm sure...but somebody smarter than me would need to explain them. I believe the WPF team really wanted to remove this restriction and they worked at it pretty hard...in the end I think the number of core OS changes need was unworkable...so they moved on....rats.

Why wasn't it possible to allow collection updates from any thread?

Is was and is possible... Making something thread-safe always costs some in performance and add complexity. In most cases the application doesn't call for multi thread access. It is important to understand that, for the most part, Microsoft plays by the same rules we do and the same restrictions. If they had made the ObservableCollection thread-safe ...they would have used the same tools we have...locks, monitors, etc. They cannot break the Ui thread rule any more than we can...no magic...same rules.

I know there are workarounds, so my question is not how to avoid the "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread" exception.

There are no workarounds...There is nothing to workaround. The ObservableCollection is broken..it is just not thread-safe. You must make it, or access to it, thread-safe. This is the same for anything that is not thread-safe...if you need it to be thread-safe then make it so. If you are using threads then you know about locks and such...use them..that is what they are for.

...block UI update when ObservableCollection is changed from other threads.... it will always work, at least if locks are used properly....

If locks are used properly...Exactly ! Again, Microsoft could have put these locks in but they didn't and for very good reasons. You can put the locks in. Or you use other tactics that will give you thread-safe access....lots of options.

The Task Parallel Library in .net4.0 provides some new tools for solving these problems. Being able to set a context for a task or a thread is particularly useful...

  // get the Ui thread context
  _uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

  Action DoInBackground = new Action(() =>
  {
    /*...In the background...
      ...process some data for an ObservableCollection...*/
  });

  Action DoOnUiThread = new Action(() =>
  { 
    /*...On the UI thread...
      ...read/write data to an ObservableCollection...*/
  });

  // start the background task
  var t1 = Task.Factory.StartNew(() => DoInBackground());
  // when t1 is done run t1..on the Ui thread.
  var t2 = t1.ContinueWith(t => DoOnUiThread(), _uiScheduler);

Don't think about the thread affinity requirements of Ui Elements as something to work around....it is just the way it works.

C# and .Net have many tools that you can use that make threading a little less of a nightmare. Use them..they can be fun.

I'm going for a smoke.

Rusty