我从哪里获得线程安全的 CollectionView? [英] Where do I get a thread-safe CollectionView?

查看:28
本文介绍了我从哪里获得线程安全的 CollectionView?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在后台线程上更新业务对象集合时,我收到此错误消息:

When updating a collection of business objects on a background thread I get this error message:

这种类型的 CollectionView 不支持从不同于 Dispatcher 线程的线程更改其 SourceCollection.

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

好的,这是有道理的.但这也引出了一个问题,CollectionView 的哪个版本支持多线程以及如何让我的对象使用它?

Ok, that makes sense. But it also begs the question, what version of CollectionView does support multiple threads and how do I make my objects use it?

推荐答案

以下是对 Jonathan 发现的实现的改进.首先,它在与其关联的调度程序上运行每个事件处理程序,而不是假设它们都在同一个 (UI) 调度程序上.其次,它使用 BeginInvoke 来允许在我们等待调度程序可用时继续处理.在后台线程进行大量更新并在每个线程之间进行处理的情况下,这使得解决方案更快.也许更重要的是,它克服了在等待 Invoke 时阻塞导致的问题(例如,在将 WCF 与 ConcurrencyMode.Single 一起使用时可能会发生死锁).

The following is an improvement on the implementation found by Jonathan. Firstly it runs each event handler on the dispatcher associated with it rather than assuming that they are all on the same (UI) dispatcher. Secondly it uses BeginInvoke to allow processing to continue while we wait for the dispatcher to become available. This makes the solution much faster in situations where the background thread is doing lots of updates with processing between each one. Perhaps more importantly it overcomes problems caused by blocking while waiting for the Invoke (deadlocks can occur for example when using WCF with ConcurrencyMode.Single).

public class MTObservableCollection<T> : ObservableCollection<T>
{
    public override event NotifyCollectionChangedEventHandler CollectionChanged;
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
        if (CollectionChanged != null)
            foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
            {
                DispatcherObject dispObj = nh.Target as DispatcherObject;
                if (dispObj != null)
                {
                    Dispatcher dispatcher = dispObj.Dispatcher;
                    if (dispatcher != null && !dispatcher.CheckAccess())
                    {
                        dispatcher.BeginInvoke(
                            (Action)(() => nh.Invoke(this,
                                new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
                            DispatcherPriority.DataBind);
                        continue;
                    }
                }
                nh.Invoke(this, e);
            }
    }
}

因为我们使用的是 BeginInvoke,所以有可能在调用处理程序之前撤消通知的更改.这通常会导致索引超出范围".当根据列表的新(更改)状态检查事件参数时抛出异常.为了避免这种情况,所有延迟事件都替换为复位事件.在某些情况下,这可能会导致过度重绘.

Because we are using BeginInvoke, it is possible that the change being notified is undone before the handler is called. This would typically result in an "Index was out of range." exception being thrown when the event arguments are checked against the new (altered) state of the list. In order to avoid this, all delayed events are replaced with Reset events. This could cause excessive redrawing in some cases.

这篇关于我从哪里获得线程安全的 CollectionView?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆