我有一个 ObservableCollection<A> a_collection;
该集合包含“n”项。每个项目 A 看起来像这样:
public class A : INotifyPropertyChanged
{
public ObservableCollection<B> b_subcollection;
Thread m_worker;
}
基本上,它都连接到一个 WPF ListView + 一个显示 b_subcollection
的详细信息 View 控件。在单独的 ListView 中选择的项目(2 向绑定(bind)、属性更新等)。
当我开始实现线程时,问题出现了。整个想法是让整个 a_collection
使用它的工作线程来“工作”,然后更新它们各自的 b_subcollections
并让 gui 实时显示结果。
当我尝试时,我得到一个异常,说只有 Dispatcher 线程可以修改 ObservableCollection,然后工作就停止了。
谁能解释这个问题,以及如何解决这个问题?
最佳答案
.NET 4.5 的新选项
从 .NET 4.5 开始,有一个内置机制可以自动同步对集合的访问并将 CollectionChanged
事件分派(dispatch)到 UI 线程。要启用此功能,您需要调用 BindingOperations.EnableCollectionSynchronization
从您的 UI 线程中。
EnableCollectionSynchronization
做了两件事:
- 记住调用它的线程,并使数据绑定(bind)管道在该线程上编码
CollectionChanged
事件。 - 在处理完编码事件之前获取集合的锁,这样运行 UI 线程的事件处理程序就不会在后台线程修改集合时尝试读取集合。
非常重要的是,这并不能解决所有问题:要确保线程安全地访问本质上不是线程安全的集合,您必须与框架合作当集合即将被修改时,从后台线程获取相同的锁。
因此正确操作所需的步骤是:
1。确定您将使用哪种锁定方式
这将决定必须使用 EnableCollectionSynchronization
的哪个重载。大多数情况下,一个简单的 lock
语句就足够了 this overload是标准选择,但如果您使用一些奇特的同步机制,还有 support for custom locks .
2。创建集合并启用同步
根据所选的锁定机制,在 UI 线程上调用适当的重载。如果使用标准的 lock
语句,您需要提供锁定对象作为参数。如果使用自定义同步,您需要提供 CollectionSynchronizationCallback
委托(delegate)和上下文对象(可以是 null
)。调用时,此委托(delegate)必须获取您的自定义锁,调用传递给它的 Action
并在返回前释放锁。
3。通过在修改之前锁定集合来配合
当您要自己修改集合时,您还必须使用相同的机制锁定该集合;在简单场景中使用传递给 EnableCollectionSynchronization
的相同锁定对象上的 lock()
执行此操作,或者在自定义场景中使用相同的自定义同步机制。
关于c# - 如何通过工作线程更新 ObservableCollection?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2091988/