从初始化列表的线程向列表添加项目

本文关键字:列表 添加 项目 初始化 线程 | 更新日期: 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线程的事件处理程序在从后台线程修改集合时不会尝试读取集合。非常重要的是,这并不能解决所有问题:为了确保线程安全访问本质上不是线程安全的集合,你必须与框架合作,在即将修改集合时从后台线程获取相同的锁。

因此,正确操作所需的步骤如下:

  1. 决定使用哪种锁

    这将决定必须使用EnableCollectionSynchronization的哪个重载。大多数情况下,一个简单的锁语句就足够了,所以这个重载是标准的选择,但是如果您正在使用一些特殊的同步机制,也可以支持自定义锁。

  2. 创建集合并启用同步

    根据所选择的锁机制,在UI线程上调用适当的重载。如果使用标准锁语句,则需要提供锁对象作为参数。如果使用自定义同步,则需要提供一个CollectionSynchronizationCallback委托和一个上下文对象(可以为null)。当被调用时,此委托必须获取您的自定义锁,调用传递给它的Action,并在返回之前释放锁。

  3. 在修改集合之前锁定集合

    当你要自己修改集合时,你也必须使用相同的机制锁定它;在简单的场景中使用传递给EnableCollectionSynchronization的锁对象上的lock(),或者在自定义的场景中使用相同的自定义同步机制。