4

我有一个难题。我“继承”了一个设计非常糟糕且非常复杂的系统,我正在(与我的团队)一块一块地对其进行现代化和重建。问题是当前系统依赖于 200 多个用户,并且由于(缺乏)设计,他们在性能方面存在重大问题。目前最有问题的问题是大量工作被放置在 UI 线程上,这导致 GUI 挂起,直到线程被清除并且消息泵可以继续。大部分工作实际上确实需要在 GUI 线程上进行,因为由于其他线程上的其他计算结果,它正在更新网格中的大量字段。

问题是这样的:我没有资源专门用于重写这里涉及的线程模型和底层类,而且这项工作的复杂性会带来很大的风险,这对我的客户来说是不可接受的。

我想知道是否有人对如何在不过多干扰当前线程模型的情况下提高 UI 性能有任何建议。

我最初的想法是,可能有一些方法可以在对 UI 线程的实际调用之前放置一个“缓冲区”,以确保 GUI 不会过载,或者当它确实退回到它的调度时。

任何建议将不胜感激。

我知道这一切都不是理想的,但我们就是我们现在的位置,我真的想在一年的重写完成之前给我的用户一个更好的体验!

谢谢!

更新 #1 这是一个 winforms 应用程序...抱歉,一开始并不清楚。新代码是 WPF,但这些模块是 winforms。

更新 #2 我想我最初可能会尝试将对 UI 线程的大多数 BeginInvoke 调用更改为 Invoke,引入一个有望提高 UI 响应能力的序列化。这里有任何(非显而易见的)缺点,任何人都可以预见?

4

3 回答 3

1

我不知道它是否适用于您的特定情况,但我过去曾处于类似(尽管压力可能较小)的情况,我想出了一些代码,让我或多或少任意编组从后台线程到 UI 线程的代码。这是在 WinForms 时代编写的。

这可能会为您提供一种风险较低的技术,让您将一些计算返回到一些后台线程,并更轻松地将您的 UI 更新编组到前台,而无需完全重新架构线程模型(或缺少线程模型)。

关于 UI 的性能,有时让它更快的最好方法是少做。当我创建链接代码时,我正在处理的应用程序正在解析数百兆字节的字符串以手动处理一些 xml。用字符串构建器替换不可变字符串使操作从 OOM 故障状态到挂钟时间的 5 分钟内完成。然后我注意到,对于它解析的每个元素,它都会更新 UI。我调整了该代码以每 50 个左右元素更新一次 ui,这将其缩短到不到一分钟。关闭所有 UI 更新将时间缩短到几秒钟。

您是否考虑过在计算完成之前不更新 UI,也许只是运行一个进度条?另一种可能性(在我的脑海中,不知道它有多邪恶)是将更新作为 lambdas 在字典中排队,该字典与控件获取更新无关。这样,如果单个控件被多次更新,您可以替换对值的冗余更新,例如(这假设应用程序没有直接从 UI 读取值来执行计算,当然。它可能是。:()

于 2011-04-28T20:09:38.313 回答
0

假设这是一个 WinForms 应用程序,一种相当老套的方法是在 UI 线程上运行的密集工作中定期调用Application.DoEvents() 。这允许消息泵在您的逻辑中处理待处理的消息。请注意,这充满了自己的一系列危险,例如:

  1. 所有 UI 事件都可以触发,这意味着您有诸如按钮按下触发长时间运行的 UI 更新之类的事情,这随后会阻止您之前使用 DoEvents() 产生的 UI 工作。这种情况的一个特别阴险的形式是当你“双击”一个按钮时,它会启动一个长操作,这会产生 DoEvents(),它会处理另一个按钮单击并启动相同的操作,该操作一直运行到完成,然后返回控制权返回到其操作中间的第一个按钮单击处理程序。这意味着您必须非常小心在 UI 线程上的这些“长时间运行”操作期间允许的 UI 交互。

  2. 因为用户可以与 UI 交互,所以他们可能会使您的代码之前所做的关于事物不变的假设无效。同样,限制用户可以执行的操作非常重要,这样他们就不会使您的操作所依赖的状态无效。当然,在另一个线程中运行的代码也会遇到同样的问题。

  3. 在 VB6 时代(多线程很难做到),这是一种相当普遍的技术,但在现代开发中却不受欢迎。它可能会让你摆脱遗留应用程序的困境,但我建议用//HACK标签标记它以供将来清理。

于 2011-04-28T20:26:15.957 回答
0

如果您正在进行大规模更新,您可以使用 CurrencyManager 的 SuspendBinding 和 ResumeBinding 方法暂停和恢复控件绑定。这样,如果由于大量 UI 交互而无法在单独的线程中移动某些内容,您仍然可以通过不显示更新来获得一些性能提升。在更新结束之前暂停任何列表更改通知可能会有所帮助,尤其是在使用网格或其他控件时。

于 2011-04-28T20:19:07.063 回答