从初始化列表的线程向列表添加项目
本文关键字:列表 添加 项目 初始化 线程 | 更新日期: 2023-09-27 18:12:42
我有一个通过WCF与服务器通信的WPF应用程序。我在远程服务器上执行一个方法,该回调方法初始化一个列表,其结果运行在不同的线程上。-这很好,这正是我申请的目的。
但是当我想添加更多的项目到这个列表时,它抛出一个异常,我不能从初始化这个列表的不同线程添加项目。
public ObservableCollection<ListBoxItemVM<T>> Items
{
get { return items; }
set
{
// This section runs on a separate thread.
items = value;
notify("Items");
if (allItems == null)
allItems = new ObservableCollection<ListBoxItemVM<T>>(items.Clone());
// I want to save the current context here and use it on the AddItem method
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(Items);
view.Filter = searchFilter;
}
}
public void AddItem(ListBoxItemVM<T>
{
this.items.Add(item); // The following exception throws here
}
异常:这种类型的CollectionView不支持从与Dispatcher线程不同的线程更改其SourceCollection。
我正在寻找一些方法来保存线程(或线程的ExecuteContext),列表被初始化,并添加项目到该列表与这个线程/上下文。
应该提到的是,这与UI线程无关,我在代码的另一个区域处理了UI线程的封送处理。我试图用UI SynchronizationContext编组this.items.Add(item);
代码,但它们不同,所以失败了。
谢谢
从。net 4.5开始,有一个内置的机制来自动同步对collection的访问,并将CollectionChanged事件分派给UI线程。要启用该特性,需要调用BindingOperations。在UI线程中启用lecollectionsynchronization
EnableCollectionSynchronization做两件事:
记住调用它的线程,并使数据绑定管道封送该线程上的CollectionChanged事件。在封送事件被处理之前,获取对集合的锁,以便运行UI线程的事件处理程序在从后台线程修改集合时不会尝试读取集合。非常重要的是,这并不能解决所有问题:为了确保线程安全访问本质上不是线程安全的集合,你必须与框架合作,在即将修改集合时从后台线程获取相同的锁。
因此,正确操作所需的步骤如下:
-
决定使用哪种锁
这将决定必须使用EnableCollectionSynchronization的哪个重载。大多数情况下,一个简单的锁语句就足够了,所以这个重载是标准的选择,但是如果您正在使用一些特殊的同步机制,也可以支持自定义锁。
-
创建集合并启用同步
根据所选择的锁机制,在UI线程上调用适当的重载。如果使用标准锁语句,则需要提供锁对象作为参数。如果使用自定义同步,则需要提供一个CollectionSynchronizationCallback委托和一个上下文对象(可以为null)。当被调用时,此委托必须获取您的自定义锁,调用传递给它的Action,并在返回之前释放锁。
-
在修改集合之前锁定集合
当你要自己修改集合时,你也必须使用相同的机制锁定它;在简单的场景中使用传递给EnableCollectionSynchronization的锁对象上的lock(),或者在自定义的场景中使用相同的自定义同步机制。