线程化和创建 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 thread new ObservableCollection 在另一个线程中是一个问题,不要在另一个线程中这样做。您可以通过调度程序 invoke 该部分,以便它将在 UI 线程中执行。 它在某种意义上回答了这个问题,因为它是正确的解决方案。由于链接的问题,我已经解决了这些问题,但我仍然对此感到好奇: If the collection = new ObservableCollection();函数被单独调用,如果 UI 线程试图访问这个集合会怎么做? 【参考方案1】:

问题实际上根本不是ObservableCollection&lt;Something&gt;,而是&lt;ItemsControl ItemsSource="Binding Settings.collection"

ObservableCollection 本身在跨多个线程访问时没有任何问题(只要这些线程一次访问一个)。普通(非DependencyProperty)属性也是如此——它们不会继承到单个线程。 WPF 绑定系统可以(在大多数情况下)处理来自其他线程的绑定属性的更改(前提是您没有使用DependencyProperty)。 不能处理来自其他线程的更改是ItemsControl

让我们逐步了解它,但首先我必须做出一些假设。首先,我必须假设 collection 实际上是一个属性,而不是一个字段(或者至少通过一个属性公开),因为您不能绑定到一个字段。其次,我必须假设涉及INotifyPropertyChanged 接口,可能是在Settings 类上实现的。

所以一步一步来:

    Settings.Modify 中,您执行collection = new ObservableCollection&lt;Something&gt;();。这成功地更新了属性。 WPF 绑定系统收到此更改的通知(通过假定的INotifyPropertyChanged 接口)并为您处理线程更改。因此,ItemsControl.ItemsSource 在 UI 线程上通过绑定更新为 collection 的新值。 ItemsControl 采用这个新的 ObservableCollection 并创建一个 CollectionView,并将其用作其项目的数据源。 回到Settings.Modify,你打电话给collection.Add(...)。这成功地将一个项目添加到 ObservableCollection&lt;Something&gt;,这会触发其 INotifyCollectionChanged.CollectionChanged 事件。 ItemsControl 创建的CollectionView 处理CollectionChanged 事件,但仍在另一个线程上,它被引发。正如异常所说,CollectionView 不“支持从不同于 Dispatcher 线程的线程更改其 SourceCollection”。所以它抛出一个异常。这个异常看起来就像来自collection.Add,但是如果你查看调用堆栈,你会发现它实际上来自更深的许多帧。 collection.Add 只是所涉及的您的代码的最深层次。

在使用ObservableCollections 和多个线程时,我建议在后台线程(如果可能)上创建完整集合,然后将完整集合传递回 UI 线程进行绑定。在您的示例中,您可以创建一个局部变量,将您的项目添加到该变量中,然后在所有项目就位后将其设置为您的 collection 属性。或者,您可以使用 Dispatcher.InvokeTask&lt;Something&gt; 将单个项目传回要添加的 UI 线程。

【讨论】:

感谢您的详细回答,这解决了问题!

以上是关于线程化和创建 ObservableCollection的主要内容,如果未能解决你的问题,请参考以下文章

子类化和覆盖使用 nib 文件创建的 UIView

java创建对象过程 实例化和初始化

Netty实战-EventLoop和线程模型

SKSpriteNode 的正确子类化和实例初始化

同一指令中的类实例化和方法? [关闭]

用于本地实例化和作用域变量的 OCUnit 或 OCmock 测试方法。