如何通过工作线程更新ObservableCollection?

12 浏览
0 Comments

问题的原因是,在.NET 4.0中,如果想要在一个工作线程中更新ObservableCollection,需要使用Dispatcher来调度更新操作。此外,需要确保Application.Current不为空。

解决方法是使用以下代码:

1. 添加元素到ObservableCollection:

Application.Current.Dispatcher.BeginInvoke(new Action(() => this.MyObservableCollection.Add(myItem)));

2. 从ObservableCollection中移除元素:

Application.Current.Dispatcher.BeginInvoke(new Func(() => this.MyObservableCollection.Remove(myItem)));

以上的代码片段使用了Dispatcher来将更新操作调度到UI线程中进行处理,以确保ObservableCollection的更新是在UI线程中进行的。这样做可以避免多线程操作导致的不一致性和异常。

需要注意的是,使用这种方法进行更新操作时,需要确保Application.Current不为空。如果Application.Current为空,可能需要先初始化Application对象,或者使用其他可用的Dispatcher对象。

在.NET 4.0中,如果想要在工作线程中更新ObservableCollection,需要使用Dispatcher来调度更新操作。通过调用Application.Current.Dispatcher.BeginInvoke方法,可以将更新操作安全地调度到UI线程中进行处理。这样做可以避免多线程操作导致的不一致性和异常。需要注意的是,使用这种方法时需要确保Application.Current不为空。

0
0 Comments

如何通过工作线程更新ObservableCollection?

问题原因:问题并不是你正在从后台线程更新ObservableCollection,问题是当你这样做时,集合在同一线程上引发了CollectionChanged事件,这意味着控件正在从后台线程更新。

解决方法:为了在控件绑定到集合时从后台线程填充集合,你可能需要从头开始创建自己的集合类型来解决这个问题。不过,还有一个简单的选项可能适合你。

将Add调用提交到UI线程。

public static void AddOnUI(this ICollection collection, T item) {
    Action addMethod = collection.Add;
    Application.Current.Dispatcher.BeginInvoke(addMethod, item);
}
...
b_subcollection.AddOnUI(new B());

这个方法会立即返回(在项实际添加到集合之前),然后在UI线程上,项将被添加到集合,这样每个人都应该满意。

然而,现实情况是,由于所有的跨线程活动,这个解决方案可能会在重负载下变慢。一个更高效的解决方案是将一堆项批量处理,并定期将它们提交到UI线程,这样你不需要为每个项调用跨线程操作。

BackgroundWorker类实现了一种模式,允许您通过其ReportProgress方法在后台操作期间报告进度。进度通过ProgressChanged事件在UI线程上报告。这可能是另一种选择。

BackgroundWorker的runWorkerAsyncCompleted绑定到UI线程吗?

是的,BackgroundWorker的设计方式是使用SynchronizationContext.Current来引发完成和进度事件。DoWork事件将在后台线程上运行。这是一篇关于WPF中线程的好文章,也讨论了BackgroundWorker msdn.microsoft.com/en-us/magazine/cc163328.aspx#S4

这个答案的简洁性令人赞叹。谢谢你的分享!

在大多数情况下,后台线程不应该阻塞并等待UI更新。使用Dispatcher.Invoke会有死锁的风险,如果两个线程最终互相等待,最好会严重影响代码的性能。在你的特定情况下,可能需要以这种方式进行,但对于绝大多数情况来说,你最后一句话是不正确的。

这种方法最终会导致难以调试的并发问题和应用程序崩溃。

0
0 Comments

如何通过工作线程更新ObservableCollection?

在.NET 4.5中,有一种内置机制可以自动同步访问集合并将CollectionChanged事件分派到UI线程。要启用此功能,您需要从UI线程调用BindingOperations.EnableCollectionSynchronization。

EnableCollectionSynchronization执行以下两个操作:

1. 记住调用它的线程,并使数据绑定管道在该线程上调度CollectionChanged事件。

2. 在处理事件的UI线程上获取对集合的锁,以防止UI线程的事件处理程序在后台线程修改集合时尝试读取集合。

然而,这并不能解决所有问题:为了确保对本质上不是线程安全的集合的线程安全访问,您必须与框架合作,在后台线程修改集合之前获取相同的锁。

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

1. 决定要使用的锁类型。这将决定使用EnableCollectionSynchronization的哪个重载。大多数情况下,简单的lock语句就足够了,所以标准选择是使用这个重载。但如果您使用一些高级同步机制,也可以使用支持自定义锁的重载。

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

3. 在修改集合之前,通过使用相同的机制对集合进行加锁来进行合作。在简单情况下,可以使用传递给EnableCollectionSynchronization的锁对象执行lock()操作;在自定义情况下,可以使用相同的自定义同步机制。

这会导致集合更新阻塞,直到UI线程处理它们吗?在涉及单向数据绑定的不可变对象集合的场景(相对常见的场景)中,似乎可以创建一个集合类,在UI线程中保留每个对象的"最后显示版本"以及更改队列,并使用BeginInvoke在UI线程中运行执行所有适当更改的方法(任何给定时间内,最多只有一个BeginInvoke处于等待状态)。

我不清楚这比根据需要调用Dispatcher更好在哪里。它们显然是不同的方法,都需要大约相同数量的额外代码-因此,对为什么一个优于另一个的解释将是有帮助的。直观上看,不调用Dispatcher会导致更可预测的失败(可以捕捉和修复),而忘记同步访问集合更改可能直到遇到具有不同时间行为的环境才会被忽略。

调用UI线程的Dispatcher有一些缺点。最大的缺点是在UI线程处理调度之前,集合不会被更新,然后您将在UI线程上运行,这可能会导致响应性问题。另一方面,使用锁定方法,您可以立即更新集合,并且可以在后台线程上继续进行处理,而不依赖于UI线程执行任何操作。UI线程将在下一个渲染周期中根据需要跟上更改。

我已经研究了4.5中的集合同步约一个月了,我不认为这个答案的一些内容是正确的。答案指出,启用调用必须发生在UI线程上,并且回调发生在UI线程上。这两个似乎都不是正确的。我能够在后台线程上启用集合同步并仍然使用这个机制。此外,框架中的深层调用不进行任何调度(参见ViewManager.AccessCollection)。

关于EnableCollectionSynchronization,这个线程的答案提供了更多的见解:stackoverflow.com/a/16511740/2887274

- "这会导致集合更新阻塞,直到UI线程处理它们吗?" 不会。它只是使UI线程在访问集合时使用lock(YourLockObject)包装其访问方式。就像您在代码中做的那样。没有什么神秘的。之所以这样做,而不是使用任何延迟更新集合的技术,是因为这样可以始终保持集合是最新的。

0