问题
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?
回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/2980642/why-isnt-it-possible-to-update-an-observablecollection-from-a-different-thread