我有一个 ObservableCollection<A> a_collection;
该集合包含“n”个项目。每个项目 A 如下所示:
public class A : INotifyPropertyChanged
{
public ObservableCollection<B> b_subcollection;
Thread m_worker;
}
基本上,它都连接到 WPF 列表视图 + 详细信息视图控件,该控件在单独的列表视图中显示所选项目的 b_subcollection
(2 路绑定、propertychanged 的更新等)。
当我开始实现线程时,问题就出现了。整个想法是让整个 a_collection
使用它的工作线程“工作”,然后更新它们各自的 b_subcollections
并让 gui 实时显示结果。
当我尝试它时,我得到一个异常,说只有 Dispatcher 线程可以修改 ObservableCollection,并且工作停止了。
谁能解释这个问题,以及如何解决它?
.NET 4.5 的新选项
从 .NET 4.5 开始,有一个内置机制可以自动同步对集合的访问并将
CollectionChanged
事件分派到 UI 线程。要启用此功能,您需要调用BindingOperations.EnableCollectionSynchronization
from within your UI thread
。EnableCollectionSynchronization
做了两件事:CollectionChanged
事件。非常重要的是,这并不能解决所有问题:为了确保对本质上不是线程安全的集合的线程安全访问您必须通过从后台线程获取相同的锁来与框架合作该集合即将被修改。
因此,正确操作所需的步骤是:
1. 决定你将使用什么样的锁定
这将确定必须使用
EnableCollectionSynchronization
的哪个重载。大多数情况下,一个简单的lock
语句就足够了,因此 this overload 是标准选择,但如果您使用一些花哨的同步机制,还有 support for custom locks 。2. 创建集合并启用同步
根据选择的锁定机制,在 UI 线程上调用适当的重载。如果使用标准的
lock
语句,您需要提供锁定对象作为参数。如果使用自定义同步,您需要提供CollectionSynchronizationCallback
委托和上下文对象(可以是null
)。调用时,此委托必须获取您的自定义锁,调用传递给它的Action
并在返回之前释放锁。3. 修改前锁定集合进行合作
当您要自己修改集合时,您还必须使用相同的机制锁定集合;在简单场景中使用
lock()
在传递给EnableCollectionSynchronization
的同一锁定对象上执行此操作,或者在自定义场景中使用相同的自定义同步机制。