UI 线程与后台线程 - UI 控制可访问性边界

UI thread vs background thread - UI control accessibility boundary

很可能我的问题无法笼统地回答,如果是的话,那无论如何都是有趣的信息。我试图更好地理解 UI 控制交互,特别是非 UI 线程允许执行的操作和不允许执行的操作。在我的例子中,我正在与 WPF 控件和位于 Microsoft.Office.Interop 命名空间中的对象(代表控件)进行交互。我在我的模型层的后台线程上做了很多工作,但是当我的视图模型接收到事件时,我在 UI 线程上调度(这通常是在处理绑定的属性时到 WPF 控件)。这对我来说很有意义,而且似乎运作良好。但是,我不确定后台线程可以做什么和不能做什么。本质上,我试图了解边界在哪里。为了发挥想象力,请看下面的一些问题。我知道我的请求有点开放性,所以如果有人知道我可以阅读的外部资源,那对我来说同样有用。

综上所述,在后台线程和UI线程之间切换时,有什么好的规则可以记住吗?例如 "any action that changes a ui control must be done on the ui thread" 或 "any action that directly interacts, regardless of action (e.g. check for null) with a ui control must be done on the ui thread".

与 UI 交互的任何东西 都不是线程安全的,试图偷工减料会给您带来麻烦。您可能认为 属性 getter 是无辜的,但事实并非如此。当线程处于 运行 时用户继续与 UI 交互时,没有什么好发生的,您将从陈旧值计算结果并呈现与 UI 不一致的结果状态。您会知道不要更改对线程执行的操作至关重要的值,您的用户不会。

没有锁可以解决这个问题,您不能在 UI 线程中加锁,也不能对用户加锁。除了显而易见的,您还需要编写代码来防止用户在线程运行时更改值。这使得在线程内部获取值变得完全没有必要,您不妨在启动线程之前获取它。将 lambda 表达式传递给线程代码非常方便。

您当然 可以 允许用户更改 UI 但是您必须编写更复杂的线程代码。您必须确保当前线程停止并启动一个新线程,现在使用更新后的值。很难做到正确,线程停止不是瞬间的,容易死锁。

同样适用于线程的结果,更新线程内的 UI 通常没有意义。 BackgroundWorker 和 Task 类 帮助您在线程完成后在 UI 线程上启动代码,它可以安全地用结果更新 UI。让线程做一些容易想通、容易环环相扣的小事,对于远离麻烦很重要。

Office 互操作与 WPF 有点不同,线程安全在 COM 中是自动的。它会自动调用 Dispatcher.Invoke() 的等价物,不需要额外的帮助。在工作线程运行时,Office 文档更有可能保持静态。除非您允许用户也更改文档,否则您会遇到 getter 问题。 YMMV.