2009-02-09 191 views

回答

31

问题在于设计一个线程安全的集合并不简单。当然,设计一个可以在不损坏状态的情况下可以从多个线程修改/读取的集合足够简单。但是设计一个可用的集合要困难得多,因为它是从多个线程更新的。以下面的代码为例。

if (myCollection.Count > 0) { 
    var x = myCollection[0]; 
} 

假设myCollection是一个线程安全的集合,其中保证添加和更新不会损坏状态。此代码不是线程安全的并且是竞争条件。

为什么?即使myCollection是安全的,但不保证在对myCollection的两个方法调用之间不会发生更改:namedly Count和索引器。另一个线程可以进入并删除这些调用之间的所有元素。

这种类型的问题使得使用这种类型的集合非常坦率地说是一场噩梦。您不能让一个调用的返回值影响集合的后续调用。

编辑

我扩大在最近的一篇博客讨论:http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx

+0

我有完全相同的问题。我如何使用Dispatcher或者从后台线程添加项目到我的BindingList? – Houman 2010-08-27 17:58:07

+0

是的,简而言之,.NET集合*接口*本身的设计不适合线程安全。正如Jared指出的那样,Count属性在多线程环境中是无用的。 – 2011-01-17 03:29:02

+0

`IList `功能有很多有用的子集,可以用线程安全的方式实现。移除东西或移动东西的项目会产生问题,但接口的其余部分对许多应用程序来说都是有用的子集。报告添加项目索引的“Add”版本将会很有帮助,但并非所有的应用程序都需要它。如果添加的每个项目在列表的生命周期中将继续存在于同一个插槽中,则线程安全的IList实现在很多多线程场景中可能非常有用,而无需外部锁定。 – supercat 2013-05-23 19:00:41

6

要一点点添加到Jared的出色答卷:线程安全没有免费的午餐。许多(大多数?)集合仅用于单个线程中。为什么这些集合有性能或功能的处罚来应对多线程的情况?

2

如果您想要发疯 - here's a ThreadedBindingList<T>会自动在UI线程上执行通知。但是,对于一个线程一次进行更新等,它仍然是安全的。

5

从所有其他的答案收集的想法,我认为这是解决问题的最简单的方法:

变化从你的问题:“为什么不是X类理智”

“什么是类X这样的理智呢?”

  1. 在类的构造函数,在创建 你的观察集合获取当前displatcher。因为你指出,修改需要在原始线程上完成 ,这个线程可能不是主要的的GUI线程。 所以App.Current。调度员不是alwasys的权利, 并不是所有的类都有this.Dispatcher

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; 
    _data = new ObservableCollection<MyDataItemClass>(); 
    
  2. 使用调度程序来调用您的代码段 需要原来的线程。

    _dispatcher.Invoke(new Action(() => { _data.Add(dataItem); })); 
    

这应该为你做的伎俩。虽然有些情况下您可能更喜欢.BeginInvoke而不是。启动

相关问题