如何处理大容量模型更新场景中无响应的UI

How to deal with unresponsive UI in high-volume model update scenarios

本文关键字:响应 UI 更新 模型 何处理 处理 大容量      更新时间:2023-10-16

我们正在使用Qt 4.8.2,我们有一个模型/视图设计(子类QAbstractItemModel和QTreeview,具体来说)。模型/树视图遵循典型的哲学,即视图驱动模型——直到用户展开相应的树视图节点,我们才填充模型。

一旦节点被展开&数据是可见的,它受制于在工作线程(非ui)中发生的显示更新。现在,当一个工作线程产生一个可能影响treeview的变化时,它会发出一个"change"信号,这个信号会映射到我们模型中的一个槽。

问题是,这些变化信号有时可以以很高的频率发出(例如,每秒1500个事件),但它们可能适用于树视图当前显示的内容(因此可以忽略)。当这种情况发生时,UI线程变得无响应,因为(我假设)信号都排队,UI线程必须在恢复响应用户交互之前处理它们。

响应更改信号所需的时间非常小,但似乎UI线程只在小延迟后"吃"信号-可能是为了避免过度更新导致屏幕闪烁或其他烦恼。

结果是UI保持冻结5或6秒,在此期间CPU活动非常低(可能是因为信号传入的速度足够快,处理程序仍在等待动作中的中断);一旦所有的信号都排队,线程最终消耗队列中的工作,并在几毫秒内解决它。

我有几个想法:

  1. 是否有一些设置使我可以增加UI线程处理传入信号的侵略性?

  2. 在一个单独的线程中管理模型的更新是否可行?我的直觉是否定的——Qt机制似乎过于依赖于模型的独占所有权,并且在其访问周围放置适当的锁保护将是复杂的,并且违反了槽/信号范式的全部观点。

  3. 我可以想出更复杂的方案来处理次要线程中的这些信号;例如,UI可以维护一个单独的多线程可见(非模型)数据结构,可以查询该数据结构以确定是否需要发送更改信号。类似地,我可以维护一个单独的队列供工作线程使用,在这个队列中,我可以将更改事件批处理为单个信号(例如,我可以每秒传递不超过两次)。但是这些方法给我的印象是,对于一个我认为在Qt UI编程领域一定不是完全罕见的问题来说,这些方法有点拜占庭式。

我们有一个类似的应用程序,对底层数据进行了大量更新。问题归结为:
每秒1500次更新会导致GUI发生多少变化?
如果答案是少于6次更改,那么模型应该每秒只发出6次数据更改。如果是这种情况,当底层数据改变时,检查这个改变是否会改变GUI,只在必要时从模型发出数据改变的信号。
如果答案是每秒将有超过6个gui变化,我们的答案是没有人可以看到每秒超过3个变化。底层数据更改根本不应该更新GUI。使用250毫秒计时器,在计时器事件中,检查需要更新的单元格,并更新它们。