C# 可观测集合多线程

C# 可观测集合多线程,c#,wpf,C#,Wpf,我有一个应用程序,其中项目从多个线程添加到集合中。 我随机得到一个 This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread. at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollecti

我有一个应用程序,其中项目从多个线程添加到集合中。 我随机得到一个

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread. at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args)
现在集合是在一个类中创建的,类本身是在多个线程上创建的

下面是一个课堂示例

public class Example
{
    public Example()
    {
        BindingOperations.EnableCollectionSynchronization(collection, COLLECTION_LOCK);

        var defaultView = CollectionViewSource.GetDefaultView(collection);
        defaultView.SortDescriptions.Add(new SortDescription("SomeProperty", ListSortDirection.Ascending));

        if (defaultView is ICollectionViewLiveShaping liveShaping)
            liveShaping.IsLiveSorting = true;
    }

    private readonly object COLLECTION_LOCK = new object();
    private readonly ObservableCollection<object> collection = new ObservableCollection<object>();

    public ObservableCollection<object> Collection
    {
        get
        {
            return collection;
        }
    }

    private void AddItem(object item)
    {
        lock(COLLECTION_LOCK)
        {
            if(!Collection.Contains(item))
            {
                Collection.Add(item);
            }
        }
    }

    private void RemoveItem(object item)
    {
        lock (COLLECTION_LOCK)
        {
            if (Collection.Contains(item))
            {
                Collection.Remove(item);
            }
        }
    }
}
集合视图标志为 System.Windows.Data.CollectionView.CollectionViewFlags.ShouldProcessCollectionChanged | System.Windows.Data.CollectionView.CollectionViewFlags.IsCurrentBeforeFirst | System.Windows.Data.CollectionView.CollectionViewFlags.IsCurrentAfterLast | System.Windows.Data.CollectionView.CollectionViewFlags.IsDynamicSystem.Windows.Data.CollectionView.CollectionViewFlags.AllowsCrossThreadChanges | System.Windows.Data.CollectionView.CollectionViewFlags.CachedIsEmpty


AllowsCrossThreadChanges是正确的

处理这一问题的最佳方法之一是将可观察到的收集全部抛弃。它的用例非常狭窄,很难解决Dispatcher问题

取而代之的是,一旦你掌握了窍门,它就会变得非常强大,使用起来非常自然:

ReadOnlyObservableCollection<TradeProxy> data;
var source = new SourceCollection<YourClass>();

source.Connect()
    .Sort(SortExpressionComparer<YourClass>.Descending(t => t.SomeProperty)) 
    .ObserveOnDispatcher()          //ensure operation is on the UI thread
    .Bind(out data)         //Populate the observable collection
    .Subscribe();

// you can do that in ANY THREAD you want and the view will update without any problems:

source.Add(yourClasse);
ReadOnlyObservableCollection数据;
var source=new SourceCollection();
source.Connect()
.Sort(SortExpressionComparer.Descending(t=>t.SomeProperty))
.ObserveOnDispatcher()//确保操作在UI线程上
.Bind(out data)//填充可观察的集合
.Subscribe();
//您可以在任何想要的线程中执行此操作,视图将更新,不会出现任何问题:
source.Add(yourClasse);

DynamicData还具有过滤功能,可以非常轻松地重新应用过滤器、分页、分组等功能,。。。。很多事情。它基于Rx,因此在处理大型集合时,您可以轻松地抛出更改,然后在单元测试中使其立即生效。

如何实现
ObservableCollection的线程安全包装

public class ObservableCollectionWrapper<T> : ICollection<T>, INotifyCollectionChanged
{
    private readonly ObservableCollection<T> _collection;
    private readonly Dispatcher _dispatcher;

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public ObservableCollectionWrapper(ObservableCollection<T> collection, Dispatcher dispatcher)
    {
        _collection = collection;
        _dispatcher = dispatcher;
        collection.CollectionChanged += Internal_CollectionChanged;
    }

    private void Internal_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        _dispatcher.Invoke(() =>
        {
            this.CollectionChanged?.Invoke(sender, e);
        });
    }

    public int Count => _collection.Count;
    /* Implement the rest of the ICollection<T> interface */
}
公共类ObservableCollectionWrapper:ICollection,INotifyCollectionChanged
{
私有只读可观察集合\u集合;
专用只读调度程序\u调度程序;
公共事件通知CollectionChangedEventHandler CollectionChanged;
公共ObservableCollectionWrapper(ObservableCollection集合,Dispatcher)
{
_收集=收集;
_调度员=调度员;
collection.CollectionChanged+=内部_CollectionChanged;
}
private void Internal_CollectionChanged(对象发送方,NotifyCollectionChangedEventArgs e)
{
_dispatcher.Invoke(()=>
{
this.CollectionChanged?.Invoke(发送方,e);
});
}
public int Count=>\u collection.Count;
/*实现ICollection接口的其余部分*/
}
用法示例:

var collectionWrapper = new ObservableCollectionWrapper<object>(collection, this.Dispatcher);
var defaultView = CollectionViewSource.GetDefaultView(collectionWrapper);
var collectionWrapper=newobserveCollectionWapper(collection,this.Dispatcher);
var defaultView=CollectionViewSource.GetDefaultView(collectionWrapper);

您应该使用
Dispatcher.Invoke
更新集合。您的问题与此可能的Dispatcher.Invoke重复?如果是这样的话,为什么会有跨线程修改的api?另外,进行修改或创建包含集合的类的代码本身不知道Dispatcher,我不想依赖它。我曾经遇到过同样的问题,您不能从其他线程更改集合。应该使用创建它的线程来修改它。@NullReference我也有同样的问题,我不知道为什么?
var collectionWrapper = new ObservableCollectionWrapper<object>(collection, this.Dispatcher);
var defaultView = CollectionViewSource.GetDefaultView(collectionWrapper);