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

发布于 2024-07-13 13:27:40 字数 148 浏览 7 评论 0原文

我一次又一次地发现自己必须编写 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

大姐,你呐 2024-07-20 13:27:40

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

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

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

北陌 2024-07-20 13:27:40

对 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?

ゝ偶尔ゞ 2024-07-20 13:27:40

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

将您的问题更改为:

“为什么 X 班不理智?”

“对 X 类执行此操作的合理方法是什么?”

  1. 在类的构造函数中,在创建时获取当前的调度程序
    您可观察到的收藏。 因为,正如您所指出的,修改需要
    原始线程上完成,该线程可能不是 GUI线程。
    所以 App.Current.Dispatcher 并不总是正确的,
    并且并非所有类都有 this.Dispatcher

    _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher; 
      _data = new ObservableCollection(); 
      
  2. 使用调度程序调用您的代码部分
    需要原始线程。

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

这应该对你有用。 尽管在某些情况下您可能更喜欢 .BeginInvoke 而不是 .Invoke

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?"

  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.
    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>();
    
  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.

§对你不离不弃 2024-07-20 13:27:40

如果您想疯狂 - 这里 一个 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文