如何使ObservableCollection线程安全?

时间:2014-04-16 11:24:26

标签: c# thread-safety xamarin observablecollection

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

我正在从不在UI线程上的ObservableCollection中添加/删除。

我有一个方法名称EnqueueReport要添加到colleciton,DequeueReport要从colleciton中删除。

步骤流程如下: -

  1. 1.无论何时请求新报告,都会收到EnqueueReport
  2. 每隔几秒调用一个方法来检查是否生成了报告(这有一个foreach循环,用于检查ObservableCollection中所有报告的生成状态)
  3. 如果生成报告,则调用DequeueReport
  4. 我在C#库中并不多。有人可以指导我吗?

4 个答案:

答案 0 :(得分:22)

从.net framwork 4.5开始,您可以使用本机收集同步。

BindingOperations.EnableCollectionSynchronization(YourCollection, YourLockObject);

YourLockObject是任何对象的实例,例如new Object();。每个系列使用一个。

这消除了一些特殊课程或任何东西的需要。只需启用并享受;)

<强> [编辑] 正如Mark和Ed的评论中所述(感谢澄清!),这样做可以减轻您锁定集合的更新,因为它只是同步集合视图绑定并且没有神奇地使集合本身就是线程安全的。 的 [/编辑]

PS:BindingOperations位于命名空间System.Windows.Data

答案 1 :(得分:16)

此处发布的解决方案Franck将在一个线程正在添加内容的情况下工作,但ObservableCollection本身(以及它所基于的List)不是线程安全的。如果多个线程正在写入集合,则可能会引入难以跟踪的错误。我编写了一个ObservableCollection版本,它使用ReaderWriteLockSlim来实现真正的线程安全。

Unfortunately, it hit the StackOverflow character limit, so here it is on PasteBin.这应该与多个读者/作者100%一致。就像常规的ObservableCollection一样,在回调中修改集合(在收到回调的线程上)是无效的。

答案 2 :(得分:10)

您可以使用ObservableConcurrentCollection类。它们位于Microsoft在Parallel Extensions Extras库中提供的包中。

您可以在Nuget社区预先构建它: https://www.nuget.org/packages/ParallelExtensionsExtras/

或者从Microsoft获取它:

https://code.msdn.microsoft.com/ParExtSamples

答案 3 :(得分:6)

您可以创建一个简单的线程友好版本的observable集合。如下所示:

 public class MTObservableCollection<T> : ObservableCollection<T>
    {
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
            if (CollectionChanged != null)
                foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
                {
                    DispatcherObject dispObj = nh.Target as DispatcherObject;
                    if (dispObj != null)
                    {
                        Dispatcher dispatcher = dispObj.Dispatcher;
                        if (dispatcher != null && !dispatcher.CheckAccess())
                        {
                            dispatcher.BeginInvoke(
                                (Action)(() => nh.Invoke(this,
                                    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
                                DispatcherPriority.DataBind);
                            continue;
                        }
                    }
                    nh.Invoke(this, e);
                }
        }
    }

现在做了一个大规模的发现&amp;替换并更改所有ObservableCollectionMTObservableCollection以及您的好消息