为什么像 BindingList 或 ObservableCollection 这样的类不是线程安全的?
我一次又一次地发现自己必须编写 BindingList 和 ObservableCollection 的线程安全版本,因为当绑定到 UI 时,这些控件无法从多个线程更改。 我想要了解的是为什么会出现这种情况 - 这是设计错误还是这种行为是故意的?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
问题是设计一个线程安全的集合并不简单。 当然,设计一个可以从多个线程修改/读取而不破坏状态的集合是足够简单的。 但设计一个可用的集合要困难得多,因为它是从多个线程更新的。 以下面的代码为例。
假设 myCollection 是一个线程安全集合,其中保证添加和更新不会破坏状态。 此代码不是线程安全的,并且是竞争条件。
为什么? 即使 myCollection 是安全的,也不能保证对 myCollection 的两个方法调用之间不会发生更改:即 Count 和索引器。 另一个线程可以介入并删除这些调用之间的所有元素。
坦率地说,此类问题使得使用此类集合成为一场噩梦。 您永远不能让一个调用的返回值影响对集合的后续调用。
编辑
我在最近的一篇博客文章中扩展了此讨论:http://blogs.msdn.com/jaredpar/archive/2009/02/11/why-are-thread-safe-collections-so-hard.aspx
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.
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
对 Jared 的出色回答补充一点:线程安全不是免费的。 许多(大多数?)集合仅在单个线程中使用。 为什么这些集合应该有性能或功能损失来应对多线程情况?
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?
收集所有其他答案的想法,我认为这是解决您的问题的最简单方法:
将您的问题更改为:
到
在类的构造函数中,在创建时获取当前的调度程序
您可观察到的收藏。 因为,正如您所指出的,修改需要
在原始线程上完成,该线程可能不是主 GUI线程。
所以 App.Current.Dispatcher 并不总是正确的,
并且并非所有类都有 this.Dispatcher。
使用调度程序调用您的代码部分
需要原始线程。
这应该对你有用。 尽管在某些情况下您可能更喜欢 .BeginInvoke 而不是 .Invoke。
Gathering ideas from all the other answers, I think this is the simplest way to resolve your issues:
Change your question from:
to
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.
Use the dispatcher to Invoke your code sections
that need the original thread.
That should do the trick for you. Though there are situations you might prefer .BeginInvoke instead of .Invoke.
如果您想疯狂 - 这里 一个
ThreadedBindingList
,自动在 UI 线程上返回通知。 然而,一次只有一个线程进行更新等操作仍然是安全的。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.