【问题标题】:Using ObservableCollection across UI and Non-UI threads跨 UI 和非 UI 线程使用 ObservableCollection
【发布时间】:2018-07-15 12:45:44
【问题描述】:

我正在使用 here 描述的 SortableObservableCollection。现在我想从 UI 或非 UI 线程操作它,所以我尝试了 here 描述的解决方案:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime;
using System.Windows.Data;

public class SortableObservableCollection<T> : ObservableCollection<T>
{
    private object _itemsLock = new object();

    public SortableObservableCollection()
        : base()
    {
        BindingOperations.EnableCollectionSynchronization(this, _itemsLock);
    }

    public SortableObservableCollection(List<T> l) : base(l)
    {
        BindingOperations.EnableCollectionSynchronization(this, _itemsLock);
    }

    public SortableObservableCollection(IEnumerable<T> l) : base(l)
    {
        BindingOperations.EnableCollectionSynchronization(this, _itemsLock);
    }
    #region Sorting

    public void Sort<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderBy(keySelector));
    }

    public void SortDescending<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderByDescending(keySelector));
    }

    public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
    {
        InternalSort(Items.OrderBy(keySelector, comparer));
    }

    private void InternalSort(IEnumerable<T> sortedItems)
    {
        var sortedItemsList = sortedItems.ToList();

        foreach (var item in sortedItemsList)
        {
            Move(IndexOf(item), sortedItemsList.IndexOf(item));
        }
    }
    #endregion // Sorting

    public new void Add(T item)
    {
        base.Add(item);
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public new void Clear()
    {
        base.Clear();
    }

    public new bool Remove(T item)
    {
        return base.Remove(item);
    }
}

但是.Net 4.5.2中的这个解决方案,WPF桌面应用,在Remove、Add、Clear方法中抛出异常:System.NotSupportedException: '这种类型的CollectionView不支持修改从与 Dispatcher 线程不同的线程到其 SourceCollection。'

所以,我重新使用 Application.Current.Dispatcher 方法(见下文),因为 BindingOperations.EnableCollectionSynchronization(this, _itemsLock); 机制不像链接的博客文章中所宣传的那样工作以上。

我的问题是:我在第一个清单中做错了什么,还是博客文章声称线程边界总是可以通过这种机制成功跨越?下面的清单是最好的,还是有更好的解决方案来管理跨线程绑定的 observablecollection?

public class SortableObservableCollection<T> : ObservableCollection<T>
{
    public SortableObservableCollection()
        : base()
    {
    }

    public SortableObservableCollection(List<T> l) : base(l)
    {
    }

    public SortableObservableCollection(IEnumerable<T> l) : base(l)
    {
    }
    #region Sorting

    public void Sort<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderBy(keySelector));
    }

    public void SortDescending<TKey>(Func<T, TKey> keySelector)
    {
        InternalSort(Items.OrderByDescending(keySelector));
    }

    public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
    {
        InternalSort(Items.OrderBy(keySelector, comparer));
    }

    private void InternalSort(IEnumerable<T> sortedItems)
    {
        var sortedItemsList = sortedItems.ToList();

        foreach (var item in sortedItemsList)
        {
            Move(IndexOf(item), sortedItemsList.IndexOf(item));
        }
    }
    #endregion // Sorting

    public new void Add(T item)
    {
        Application.Current.Dispatcher.Invoke(() =>
        {
            base.Add(item);
        }, System.Windows.Threading.DispatcherPriority.DataBind);
    }

    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
    public new void Clear()
    {
        Application.Current.Dispatcher.Invoke(() =>
        {
            base.Clear();
        }, System.Windows.Threading.DispatcherPriority.DataBind);
    }

    public new bool Remove(T item)
    {
        return Application.Current.Dispatcher.Invoke(() =>
        {
            return base.Remove(item);
        }, System.Windows.Threading.DispatcherPriority.DataBind);
    }
}

【问题讨论】:

  • Move 方法似乎不是线程安全的
  • 那么,这是什么意思?我可以让它线程安全吗?是使用 Dispaytcher 的第二个解决方案,因此,更好的方法?
  • 我无法决定哪个更好,也不是发表意见的地方。在处理多线程应用程序时,您应该始终采取额外措施以避免此异常。您应该清楚地了解每个线程上发生的事情,并在必要时放置invoke
  • 我认为BindingOperations.EnableCollectionSynchronization 应该只使用一次

标签: c# .net wpf multithreading observablecollection


【解决方案1】:

我的问题是:我在第一个清单中做错了什么还是博客文章不正确地声称线程边界总是可以通过这种机制成功跨越?

问题是您需要在 UI 线程上调用 BindingOperations.EnableCollectionSynchronization 方法,即您需要在 UI 线程上实例化您的 SortableObservableCollection&lt;T&gt; 才能使此方法起作用。

如果您不能保证集合将在 UI 线程上初始化,您应该使用调度程序将所有修改数据绑定集合的操作封送回 UI 线程。在后台线程上调用 BindingOperations.EnableCollectionSynchronization 不会解决您的问题。

【讨论】:

  • 好的,这是有道理的。我检查了我的代码,确实有非 UI 线程创建了上述集合的实例。当我包装对 BindingOperations.EnableCollectionSynchronization(this, _itemsLock); 的每个调用时,使用调度程序调用 BindingOperations 方法可以使这项工作正常进行。进入 Current.Dispatcher,Invoke(...) 调用。非常感谢,我不知道这个限制,大多数网站都没有在任何地方提到它......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-17
相关资源
最近更新 更多