简体   繁体   English

为什么BindingList或ObservableCollection这样的类不是线程安全的?

[英]Why aren't classes like BindingList or ObservableCollection thread-safe?

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. 一次又一次我发现自己必须编写BindingList和ObservableCollection的线程安全版本,因为当绑定到UI时,不能从多个线程更改这些控件。 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. 假设myCollection是一个线程安全的集合,其中保证添加和更新不会破坏状态。 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. 即使myCollection是安全的,也不能保证在对myCollection的两个方法调用之间不会发生更改:命名为Count和索引器。 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 我在最近的博客文章中扩展了这个讨论: http//blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

To add a little to Jared's excellent answer: thread safety does not come for free. 为Jared的优秀答案添加一点:线程安全不是免费的。 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?" “为什么X级不健全?”

to

"What is the sane way of doing this with class X?" “用X级这样做的理智是什么?”

  1. 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. 正如您所指出的那样,需要在原始线程上进行修改, 原始线程可能不是主要的 GUI线程。 So App.Current.Dispatcher isn't alwasys right, and not all classes have a this.Dispatcher . 所以App.Current.Dispatcher并不是alwasys ,并非所有类都有this.Dispatcher

     _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; _data = new ObservableCollection<MyDataItemClass>(); 
  2. 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 . 虽然有些情况你可能更喜欢.BeginInvoke而不是.Invoke

If you want to go crazy - here's a ThreadedBindingList<T> that does notifications back on the UI thread automatically. 如果你想发疯 - 这是一个ThreadedBindingList<T> ,它会自动在UI线程上回复通知。 However, it would still only be safe for one thread to be making updates etc at a time. 但是,对于一个线程一次进行更新等仍然是安全的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM