为了账号安全,请及时绑定邮箱和手机立即绑定

如何通过工作线程更新观察者集合?

如何通过工作线程更新观察者集合?

C#
月关宝盒 2019-07-17 15:24:13
如何通过工作线程更新观察者集合?我有一个ObservableCollection<A> a_collection;集合包含“n”项。每一项A如下所示:public class A : INotifyPropertyChanged{     public ObservableCollection<B> b_subcollection;     Thread m_worker;}基本上,它都连接到一个WPF列表视图+一个细节视图控件,该控件在单独的listview(双向绑定、属性更改更新等)中显示所选项的b_子集合。当我开始实现线程时,问题就出现了。整个想法是让整个a_集合使用它的辅助线程“做工作”,然后更新它们各自的b_子集合,让gui实时显示结果。当我尝试它时,我得到了一个异常,它说只有Dispatcher线程才能修改一个观测者集合,然后工作就停止了。有谁能解释这个问题,以及如何解决这个问题?干杯
查看完整描述

3 回答

?
慕容708150

TA贡献1831条经验 获得超4个赞

从技术上讲,问题不是要从后台线程中更新观测者集合。问题是,当您这样做时,集合会在导致更改的同一线程上引发它的Collection Changed事件-这意味着正在从后台线程更新控件。

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

将添加调用发布到UI线程。

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

此方法将立即返回(在项目实际添加到集合之前),然后在UI线程上,项目将被添加到集合中,每个人都应该很高兴。

然而,现实情况是,由于所有的跨线程活动,这种解决方案很可能会在沉重的负荷下陷入困境。一个更有效的解决方案是对一组项目进行批处理,并定期将它们发布到UI线程中,这样您就不会为每个项目跨线程进行调用。

这个背景工作者类实现了允许您通过其报告进度方法在后台操作期间。进度通过ProgressChanged事件报告在UI线程上。这可能是你的另一个选择。


查看完整回答
反对 回复 2019-07-17
?
HUWWW

TA贡献1874条经验 获得超12个赞

.NET 4.5的新选项

从.NET 4.5开始,有一个内置机制来自动同步对集合和分派的访问CollectionChanged事件到UI线程。要启用此功能,需要调用BindingOperations.EnableCollectionSynchronization 在您的UI线程中.

EnableCollectionSynchronization做两件事:

  1. 记住调用它的线程,并导致数据绑定管道封送处理。

    CollectionChanged

    线程上的事件。
  2. 在处理封送事件之前获取集合上的锁,以便运行UI线程的事件处理程序在从后台线程修改集合时不会尝试读取集合。

非常重要的是,这并不能解决所有的问题。*确保线程安全访问固有的非线程安全集合。你必须合作通过从后台线程获取相同的锁来使用框架,当集合即将被修改时。

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

1.决定使用哪种锁

这将确定EnableCollectionSynchronization一定要用。大多数时候很简单lock这样的话就足够了这种过载是标准的选择,但是如果您使用的是一些花哨的同步机制,那么支持自定义锁.

2.创建集合并启用同步

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

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

您还必须在自己修改集合时使用相同的机制锁定集合;lock()在传递给EnableCollectionSynchronization在简单场景中,或者在自定义场景中使用相同的自定义同步机制。


查看完整回答
反对 回复 2019-07-17
?
梦里花落0921

TA贡献1772条经验 获得超6个赞

使用.NET 4.0,您可以使用以下一行程序:

.Add

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

.Remove

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



查看完整回答
反对 回复 2019-07-17
  • 3 回答
  • 0 关注
  • 429 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信