确保在WPF中的UI线程上运行

sti*_*k81 36 .net wpf invoke dispatcher

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

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

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

所以; 在WPF中解决问题的关键是什么?我在这篇MSDN帖子中读到了WPF Dispatcher .我开始明白了,但我还没有巫师.

  1. 当我需要运行我不确定将在UI线程上调用的东西时,总是使用Dispatcher.Invoke的关键是什么?
  2. 如果它实际上是在UI线程上调用的,它是否重要,我还是做Dispatcher.Invoke?
  3. Dispatcher.Invoke =同步.Dispathcher.BeginInvoke = async?
  4. Dispatcher.Invoke会请求UI线程,然后停止等待吗?是不良做法和风险较低的程序?
  5. 我怎样才能得到调度员?Dispatcher.CurrentDispatcher会不会给我代表UI线程的调度程序?
  6. 是否存在多个Dispatcher,或者"Dispatcher"与应用程序的UI线程基本相同?
  7. 和BackgroundWorker有什么关系?我什么时候使用它呢?我认为这总是异步的?
  8. 在UI公寓模式下运行在UI线程上运行的所有内容(通过调用)吗?即如果我有需要在STA模式下运行的东西 - Dispatcher.Invoke会足够吗?

有人想帮我清理一下吗?任何相关的建议等?谢谢!

Cha*_*lie 37

一个接一个地回答你的每个问题:

  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要说,我相信很多问题都已经投入使用,所以我不会深入探讨.如果您很好奇,请查看BackgroundWorker类MSDN页面.

  • 对#5和#6的另一个澄清:从概念上讲,每个线程都有自己的Dispatcher(1-1关系).Dispatcher.CurrentDispatcher返回当前线程的Dispatcher.如果从UI线程调用它将返回该线程的Dispatcher.如果从后台线程调用,它将返回后台线程的Dispatcher.应用程序可以有多个UI线程,因此访问DispatcherObject的最安全方法是通过其DispatcherObject.Dispatcher属性.但是如果你的应用程序只有一个UI线程,Application.Current.Dispatcher就可以了. (11认同)
  • 好的答案(+1),但我不同意你的部分答案#2.调用Dispatcher.CheckAccess几乎总是一个坏主意.如果您想要发送优先级,只需使用它:Dispatcher.Invoke将识别它在可能的情况下直接调用.另一方面,如果您的任务优先级较低,则无论如何都不想调用CheckAccess.唯一的例外是,如果您想根据上下文改变任务的优先级.如果是这样,最好的方法是:`Dispatcher.Invoke(Dispatcher.CheckAccess()?DispatcherPriority.Send:DispatcherPriority.Render,...)` (2认同)