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