UI 线程与后台线程 - UI 控制可访问性边界
UI thread vs background thread - UI control accessibility boundary
很可能我的问题无法笼统地回答,如果是的话,那无论如何都是有趣的信息。我试图更好地理解 UI 控制交互,特别是非 UI 线程允许执行的操作和不允许执行的操作。在我的例子中,我正在与 WPF 控件和位于 Microsoft.Office.Interop 命名空间中的对象(代表控件)进行交互。我在我的模型层的后台线程上做了很多工作,但是当我的视图模型接收到事件时,我在 UI 线程上调度(这通常是在处理绑定的属性时到 WPF 控件)。这对我来说很有意义,而且似乎运作良好。但是,我不确定后台线程可以做什么和不能做什么。本质上,我试图了解边界在哪里。为了发挥想象力,请看下面的一些问题。我知道我的请求有点开放性,所以如果有人知道我可以阅读的外部资源,那对我来说同样有用。
- 我可以在 属性 上
get()
从后台线程绑定到 ui 吗,即是否只有 set()
必须从 [=35] 完成=]线程?
- 如果我有一个互操作对象,比如 Microsoft.Office.Interop.Slide,在我看来我必须在 UI 线程上与它交互,因为它被认为是一个实际的 ui 控制。但是我可以在非 ui 线程中监听 return 此类对象的事件吗?我可以从非 ui 线程检查幻灯片是否为 null 吗?我可以
get()
来自非 ui 线程的幻灯片类型中包含的属性吗?
综上所述,在后台线程和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.
很可能我的问题无法笼统地回答,如果是的话,那无论如何都是有趣的信息。我试图更好地理解 UI 控制交互,特别是非 UI 线程允许执行的操作和不允许执行的操作。在我的例子中,我正在与 WPF 控件和位于 Microsoft.Office.Interop 命名空间中的对象(代表控件)进行交互。我在我的模型层的后台线程上做了很多工作,但是当我的视图模型接收到事件时,我在 UI 线程上调度(这通常是在处理绑定的属性时到 WPF 控件)。这对我来说很有意义,而且似乎运作良好。但是,我不确定后台线程可以做什么和不能做什么。本质上,我试图了解边界在哪里。为了发挥想象力,请看下面的一些问题。我知道我的请求有点开放性,所以如果有人知道我可以阅读的外部资源,那对我来说同样有用。
- 我可以在 属性 上
get()
从后台线程绑定到 ui 吗,即是否只有set()
必须从 [=35] 完成=]线程? - 如果我有一个互操作对象,比如 Microsoft.Office.Interop.Slide,在我看来我必须在 UI 线程上与它交互,因为它被认为是一个实际的 ui 控制。但是我可以在非 ui 线程中监听 return 此类对象的事件吗?我可以从非 ui 线程检查幻灯片是否为 null 吗?我可以
get()
来自非 ui 线程的幻灯片类型中包含的属性吗?
综上所述,在后台线程和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.