线程化和创建 ObservableCollection
Posted
技术标签:
【中文标题】线程化和创建 ObservableCollection【英文标题】:Threading and creating ObservableCollection 【发布时间】:2021-01-30 23:21:09 【问题描述】:我想了解一下 C# (WPF) 在代码背后的工作原理。 情况(为简单起见,显然是假设的):
在我们的 UI 线程上,我们创建了 Settings 对象。它有一个变量:
public ObservableCollection<Something> collection;
我们从不同的后台线程调用
try
Settings.Modify()
然后执行以下操作:
Settings.Modify()
collection = new ObservableCollection<Something>();
collection.Add(...)
现在,这当然会在 Add 函数中引发异常:
这种类型的 CollectionView 不支持对其进行更改 SourceCollection 来自与 Dispatcher 线程不同的线程。
但是集合 = new ObservableCollection();行执行。据我所知,这个新集合是在后台线程的堆栈上创建的。 WPF UI 绑定到这个变量:
<ItemsControl ItemsSource="Binding Settings.collection"
在我的理解中,UI 线程应该不能再访问这个集合了。如果它尝试会发生什么,它会因为某种原因阻塞 UI 线程吗?
其他问题:为什么编译器让我用后台线程上的另一个对象替换一个明显属于 UI 线程的对象,即使它知道我在尝试从另一个线程添加到该集合时搞砸了.
编辑:
这个问题的解决方案已经回答here
:
我仍然对此感到好奇:如果
collection = new ObservableCollection<Something>();
函数被单独调用,如果UI线程试图访问这个集合会做什么?
【问题讨论】:
这能回答你的问题吗? Updating an ObservableCollection in a separate threadnew ObservableCollection
在另一个线程中是一个问题,不要在另一个线程中这样做。您可以通过调度程序 invoke 该部分,以便它将在 UI 线程中执行。
它在某种意义上回答了这个问题,因为它是正确的解决方案。由于链接的问题,我已经解决了这些问题,但我仍然对此感到好奇: If the collection = new ObservableCollection问题实际上根本不是ObservableCollection<Something>
,而是<ItemsControl ItemsSource="Binding Settings.collection"
。
ObservableCollection
本身在跨多个线程访问时没有任何问题(只要这些线程一次访问一个)。普通(非DependencyProperty
)属性也是如此——它们不会继承到单个线程。 WPF 绑定系统可以(在大多数情况下)处理来自其他线程的绑定属性的更改(前提是您没有使用DependencyProperty
)。 不能处理来自其他线程的更改是ItemsControl
。
让我们逐步了解它,但首先我必须做出一些假设。首先,我必须假设 collection
实际上是一个属性,而不是一个字段(或者至少通过一个属性公开),因为您不能绑定到一个字段。其次,我必须假设涉及INotifyPropertyChanged
接口,可能是在Settings
类上实现的。
所以一步一步来:
-
在
Settings.Modify
中,您执行collection = new ObservableCollection<Something>();
。这成功地更新了属性。 WPF 绑定系统收到此更改的通知(通过假定的INotifyPropertyChanged
接口)并为您处理线程更改。因此,ItemsControl.ItemsSource
在 UI 线程上通过绑定更新为 collection
的新值。
ItemsControl
采用这个新的 ObservableCollection
并创建一个 CollectionView
,并将其用作其项目的数据源。
回到Settings.Modify
,你打电话给collection.Add(...)
。这成功地将一个项目添加到 ObservableCollection<Something>
,这会触发其 INotifyCollectionChanged.CollectionChanged
事件。
ItemsControl
创建的CollectionView
处理CollectionChanged
事件,但仍在另一个线程上,它被引发。正如异常所说,CollectionView
不“支持从不同于 Dispatcher 线程的线程更改其 SourceCollection”。所以它抛出一个异常。这个异常看起来就像来自collection.Add
,但是如果你查看调用堆栈,你会发现它实际上来自更深的许多帧。 collection.Add
只是所涉及的您的代码的最深层次。
在使用ObservableCollection
s 和多个线程时,我建议在后台线程(如果可能)上创建完整集合,然后将完整集合传递回 UI 线程进行绑定。在您的示例中,您可以创建一个局部变量,将您的项目添加到该变量中,然后在所有项目就位后将其设置为您的 collection
属性。或者,您可以使用 Dispatcher.Invoke
或 Task<Something>
将单个项目传回要添加的 UI 线程。
【讨论】:
感谢您的详细回答,这解决了问题!以上是关于线程化和创建 ObservableCollection的主要内容,如果未能解决你的问题,请参考以下文章