从多个后台线程更新UI

时间:2012-03-16 09:42:32

标签: wpf multithreading invoke dispatcher windows-applications

这里的问题有点抽象。我们都知道,对于后台线程来更新一些UI元素。

Dispatcher.Invoke()

是唯一的选择(是吗?)。但是,Dispatcher.Invoke()本身将更新任务委托给UI线程。考虑以下情况:

  • 后台线程经常更新UI。
  • 数十个主题更新相同的用户界面。

Dispatcher对象将继续将更新任务委派给UI线程,并且UI线程可能会变慢。什么是可能的解决方案?我们怎样才能在 Windows Forms 中解决这样的问题,其中线程模型与WPF非常相似? WPF是否提供任何其他线程技术?

的问候,

3 个答案:

答案 0 :(得分:2)

如果通过发布消息(APC,代理,等等)来重载GUI线程输入队列,比处理它们更快,那么无论WPF / Forms /是什么都会出现问题。

在多个GUI状态频繁更新的情况下,发布每个更改都会使队列超载和/或导致显示变化太快而无法读取,因此非GUI线程通常会存储它们共享对象中的最新数据,以及GUI线程以更合理的间隔使用计时器显示数据。

如果必须显示所有数据而不丢失(例如,GUI备忘录或HTML呈现器必须显示发送的所有内容),则可能需要通过对象池进行适当的流量控制和/或延迟发布数据以防止消息 - 超载。

'这里的问题有点抽象' - 不是那么抽象。我使用繁忙的应用程序多次遇到此问题。在我的东西中,延长的输入队列重载往往会导致死锁,因为我的线程间通信池中的所有对象都被卡在未处理并发布到池中的已发布消息中,因为GUI被阻止 - 尝试获取对象从空池:((

答案 1 :(得分:1)

首先,为什么需要更新人眼不会注意到的频繁的UI,理想情况下,即使任何进度以一秒的间隙更新,也是可以接受的。例如,如果您正在编写一个文件,并且您可能每30毫秒写一次4K字节,那么人眼就不会注意到,我们也不会在几毫秒内关注屏幕上的性能。

不仅你会使UI线程忙,而且Dispatcher.Invoke也会阻止你的其他线程,直到执行完成,Dispatcher.BeginInvoke不会阻止你的其他线程。

如果只是在屏幕上更新少量标签或进度,UI线程可能会在几毫秒内完成更新。

WPF和任何其他平台都不能提供更好的方法,因为UI非常复杂,允许从多个线程访问可能导致死锁,因此应用程序可能会变得无响应。这就是原因,每个平台,无论是java,objective-c还是任何UI框架,都只需要在UI的创建者线程中更新UI。

但是,WPF中还有一种方法可以为每个窗口创建多个UI线程,但它也非常复杂。游戏等使用称为双缓冲的东西,它们在单独的线程中在后台运行一个缓冲区,而前一个缓冲区仍然在屏幕上更新。

答案 2 :(得分:1)

简单,不要使用Invoke来更新UI。相反,让工作线程将需要“发送到UI线程”的数据发布到共享数据结构(如队列或列表)中,并在特定时间间隔内对其进行UI线程轮询。这有几个好处。

  • 它打破了Invoke强加的UI和工作线程之间的紧密耦合。
  • 当UI控件得到更新时,UI线程会指示...当你真正想到它时应该如何。
  • 如果从工作线程中使用InvokeBeginInvoke,则不存在超出UI消息队列或使其他消息无法使用的风险。
  • 工作线程不必等待来自UI线程的响应,就像Invoke一样。
  • 您可以在UI和工作线程上获得更多吞吐量。
  • InvokeBeginInvoke是昂贵的操作。

我知道我一直都在努力,但在很多情况下,Invoke确实有更好的选择。像Invoke这样的编组技术是方式过度使用。