【问题标题】:Ensuring that things run on the UI thread in WPF确保事物在 WPF 中的 UI 线程上运行
【发布时间】:2010-03-04 20:57:12
【问题描述】:

我正在构建一个 WPF 应用程序。我正在与服务器端进行一些异步通信,并在客户端使用事件聚合和 Prism。这两件事都会导致生成不是 UI 线程的新线程。如果我尝试在这些回调和事件处理程序线程上执行“WPF 操作”,那么世界将分崩离析,现在它已经开始这样做了。

首先我遇到了尝试在来自服务器的回调中创建一些 WPF 对象的问题。有人告诉我线程需要在 STA 模式下运行。现在我正在尝试在 Prism 事件处理程序中更新一些 UI 数据,我被告知:

调用者无法访问该线程,因为另一个线程拥有它。

所以; 在 WPF 中正确处理事情的关键是什么?我已经阅读了 this MSDN post 中的 WPF Dispatcher。我开始明白了,但我还不是巫师。

  1. 当我需要运行一些我不确定是否会在 UI 线程上调用的东西时,总是使用 Dispatcher.Invoke 的关键是什么?
  2. 如果它实际上是在 UI 线程上调用的,并且我仍然执行 Dispatcher.Invoke,这是否重要?
  3. Dispatcher.Invoke = 同步。 Dispathcher.BeginInvoke = 异步?
  4. Dispatcher.Invoke 会请求 UI 线程,然后停止等待吗?响应速度较慢的程序是不好的做法和风险吗?
  5. 我如何获得调度员? Dispatcher.CurrentDispatcher 是否总是给我代表 UI 线程的调度程序?
  6. 会存在多个Dispatcher,还是“Dispatcher”与应用程序的UI线程基本相同?
  7. BackgroundWorker 是怎么回事?我什么时候改用这个?我认为这总是异步的?
  8. UI 线程上运行的所有内容(通过被调用)是否会在 STA 单元模式下运行? IE。如果我有需要在 STA 模式下运行的东西 - Dispatcher.Invoke 就足够了吗?

有人想帮我澄清一下吗?有没有相关推荐之类的?谢谢!

【问题讨论】:

    标签: .net wpf invoke dispatcher


    【解决方案1】:

    逐个回答你的每一个问题:

    1. 不完全;您应该只在必要时调用 UI 线程。请参阅 #2。
    2. 是的,这很重要。你不应该只是自动Invoke 一切。关键是仅在必要时调用 UI 线程。为此,您可以使用Dispatcher.CheckAccess 方法。
    3. 没错。
    4. 同样正确,是的,您确实会冒响应速度较慢的程序的风险。大多数时候,您不会看到严重的性能损失(我们谈论的是上下文切换的毫秒数),但如果有必要,您应该只使用Invoke。话虽如此,在某些时候这是不可避免的,所以不,我不会说这是不好的做法。这只是您不时遇到的问题的一种解决方案。
    5. 在我所看到的每一个案例中,我都已经完成了Dispatcher.CurrentDispatcher。对于复杂的场景,这可能还不够,但我(个人)还没有见过。
    6. 不完全正确,但这种思路不会造成任何伤害。让我这样说吧:Dispatcher可用于获得对应用程序 UI 线程的访问权。但它本身并不是 UI 线程。
    7. BackgroundWorker 通常在您有一个耗时的操作并希望在后台运行该操作时保持响应式 UI 时使用。通常你不使用BackgroundWorker 代替 Invoke,而是将BackgroundWorker Invoke 结合使用。也就是说,如果您需要更新 BackgroundWorker 中的某些 UI 对象,您可以调用 UI 线程,执行更新,然后返回原始操作。
    8. 是的。根据定义,WPF 应用程序的 UI 线程必须在单线程单元中运行。

    关于BackgroundWorker 有很多话要说,我相信很多问题已经涉及到它,所以我不会太深入。如果您好奇,请查看MSDN page for BackgroundWorker class

    【讨论】:

    • 非常感谢查理!这真的很清楚也很有帮助!
    • 在列表中添加了第 8 个问题。我希望您可以更新您的答案以包含它:-)
    • 好答案 (+1),但我不同意您的部分答案 #2。调用 Dispatcher.CheckAccess 几乎总是一个坏主意。如果您想要发送优先级,只需使用它:Dispatcher.Invoke 将识别它并在可能的情况下进行直接调用。另一方面,如果您的任务优先级较低,您无论如何都不想调用 CheckAccess。唯一的例外是如果您想根据上下文改变任务的优先级。如果是这样,最好的方法是:Dispatcher.Invoke(Dispatcher.CheckAccess() ? DispatcherPriority.Send : DispatcherPriority.Render, ...)
    • 对 #5 和 #6 的另一个澄清:从概念上讲,每个线程都有自己的 Dispatcher(1-1 关系)。 Dispatcher.CurrentDispatcher 返回当前线程的 Dispatcher。如果从 UI 线程调用,它将返回该线程的 Dispatcher。如果从后台线程调用,它将改为返回后台线程的 Dispatcher。一个应用程序可以有多个 UI 线程,因此访问 DispatcherObject 最安全的方法是通过它的 DispatcherObject.Dispatcher 属性。但如果您的应用程序只有一个 UI 线程,Application.Current.Dispatcher 就可以解决问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-19
    • 2021-03-07
    • 2012-05-21
    • 1970-01-01
    • 2023-03-12
    相关资源
    最近更新 更多