1

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

Dispatcher.Invoke()

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

  • 后台线程过于频繁地更新 UI。
  • 数十个线程更新相同的 UI。

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

问候,

4

3 回答 3

2

如果您通过发布消息(APC、委托等)使 GUI 线程输入队列超载,其处理速度超过了它们的处理速度,那么无论 WPF/Forms/其他什么,都会出现问题。

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

如果必须无损显示所有数据(例如,GUI 备忘录或 HTML 渲染器必须显示发送的所有内容),则可能需要通过对象池进行适当的流控制和/或延迟发布数据以防止消息队列过载.

“这里的问题有点抽象”——不是那么抽象。我用繁忙的应用程序多次遇到这个问题。在我的工作中,长时间的输入队列过载往往会导致死锁,因为我的线程间通信池中的所有对象都卡在已发布的消息中,这些消息由于 GUI 被阻塞而没有被处理并释放到池中 - 试图获取一个对象从空池中:((

于 2012-03-16T10:18:14.227 回答
1

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

  • 它打破了 UI 和工作线程之间的紧密耦合Invoke
  • UI 线程决定何时更新 UI 控件……当您真正考虑它时,它应该是这样的。
  • 不存在溢出 UI 消息队列或耗尽其他消息的风险,如果InvokeBeginInvoke从工作线程中使用,就会出现这种情况。
  • 工作线程不必像Invoke.
  • 您可以在 UI 和工作线程上获得更多吞吐量。
  • Invoke并且BeginInvoke是昂贵的操作。

我知道我一直都在谈论这件事,但Invoke在许多情况下确实有更好的选择。像这样的编组技术Invoke被过度使用了。

于 2012-03-16T13:20:35.330 回答
1

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

不仅你会让 UI 线程忙,而且 Dispatcher.Invoke 也会阻塞你的另一个线程直到执行完成, Dispatcher.BeginInvoke 不会阻塞你的另一个线程。

如果 UI Thread 只是更新几个标签或屏幕上的进度,它可能会在几毫秒内完成更新。

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

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

于 2012-03-16T10:32:51.447 回答