Time and time again I find myself having to write thread-safe versions of BindingList and ObservableCollection because, when bound to UI, these controls cannot be changed from multiple threads. What I'm trying to understand is why this is the case - is it a design fault or is this behavior intentional?
The problem is designing a thread safe collection is not simple. Sure it's simple enough to design a collection which can be modified/read from multiple threads without corrupting state. But it's much more difficult to design a collection that is usable given that it's updated from multiple threads. Take the following code as an example.
if ( myCollection.Count > 0 ) {
var x = myCollection[0];
}
Assume that myCollection is a thread safe collection where adds and updates are guaranteed not to corrupt state. This code is not thread safe and is a race condition.
Why? Even though myCollection is safe, there is no guarantee that a change does not occur between the two method calls to myCollection: namedly Count and the indexer. Another thread can come in and remove all elements between these calls.
This type of problem makes using a collection of this type quite frankly a nightmare. You can't ever let the return value of one call influence a subsequent call on the collection.
EDIT
I expanded this discussion on a recent blog post: http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx
IList<T>
functionality which could be implemented in thread-safe fashion. The items that remove things or move things around would be problematic, but the rest of the interface would be a useful subset for many applications. A version of Add
which reported the index of the item added would be helpful, but not all applications would need it. If every item that is ever added will continue to exist in the same slot for the life of the list, a thread-safe IList<T>
implementation could be useful in many multi-threaded scenarios without external locking. –
Embolectomy To add a little to Jared's excellent answer: thread safety does not come for free. Many (most?) collections are only used within a single thread. Why should those collections have performance or functionality penalties to cope with the multi-threaded case?
Gathering ideas from all the other answers, I think this is the simplest way to resolve your issues:
Change your question from:
"Why isn't class X sane?"
to
"What is the sane way of doing this with class X?"
in your class's constructor, get the current displatcher as you create your observable collections. Becuase, as you pointed out, modification need to be done on the original thread, which may not be the main GUI thread. So App.Current.Dispatcher isn't alwasys right, and not all classes have a this.Dispatcher.
_dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; _data = new ObservableCollection<MyDataItemClass>();
Use the dispatcher to Invoke your code sections that need the original thread.
_dispatcher.Invoke(new Action(() => { _data.Add(dataItem); }));
That should do the trick for you. Though there are situations you might prefer .BeginInvoke instead of .Invoke.
If you want to go crazy - here's a ThreadedBindingList<T>
that does notifications back on the UI thread automatically. However, it would still only be safe for one thread to be making updates etc at a time.
© 2022 - 2024 — McMap. All rights reserved.