我一次又一次地发现自己必须编写BindingList和ObservableCollection的线程安全版本,因为当绑定到UI时,这些控件不能从多个线程更改。我想要了解的是为什么这种情况 - 是设计错误还是故意这种行为?
答案 0 :(得分:31)
问题是设计线程安全集合并不简单。当然,设计一个可以从多个线程修改/读取而不会破坏状态的集合非常简单。但是设计一个可用的集合要困难得多,因为它是从多个线程更新的。以下面的代码为例。
if ( myCollection.Count > 0 ) {
var x = myCollection[0];
}
假设myCollection是一个线程安全的集合,其中保证添加和更新不会破坏状态。此代码不是线程安全的,并且是竞争条件。
为什么呢?即使myCollection是安全的,也不能保证在对myCollection的两个方法调用之间不会发生更改:命名为Count和索引器。另一个线程可以进入并删除这些调用之间的所有元素。
这种类型的问题使得使用这种类型的集合坦率地说是一场噩梦。您不能让一个调用的返回值影响对集合的后续调用。
编辑
我在最近的一篇博文中扩展了这个讨论:http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx
答案 1 :(得分:6)
为Jared的优秀答案添加一点:线程安全不是免费的。许多(大多数?)集合仅在单个线程中使用。为什么这些集合应该具有性能或功能处罚以应对多线程案例?
答案 2 :(得分:5)
从所有其他答案中收集想法,我认为这是解决问题的最简单方法:
更改您的问题:
“为什么X级不健全?”
到
“使用X级这样做的理智是什么?”
在你班级的构造函数中,在创建时获取当前的置换器 你的可观察的收藏品。因为你指出,修改需要 可以在原始线程上完成,该线程可能不是主 GUI线程。 所以 App.Current.Dispatcher 不是alwasys对, 并非所有课程都有 this.Dispatcher 。
_dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
_data = new ObservableCollection<MyDataItemClass>();
使用调度程序调用代码部分 需要原始线程。
_dispatcher.Invoke(new Action(() => { _data.Add(dataItem); }));
那应该为你做的伎俩。虽然在某些情况下您可能更喜欢 .BeginInvoke 而不是 .Invoke 。
答案 3 :(得分:2)
如果你想发疯 - here's一个ThreadedBindingList<T>
会自动在UI线程上发回通知。但是,一个线程一次只能安全地进行更新仍然是安全的。