进度条和多线程,解耦GUI和逻辑 - 哪种设计模式最好?

5

我正在寻找一个适合我的应用程序设计的设计模式。

我的应用程序处理大量数据并生成一些图形。数据处理(从文件获取,CPU密集型计算)和图形操作(绘制,更新)在不同的线程中进行。

图形可以滚动 - 在这种情况下,需要处理新的数据部分。因为图形上可能有几个系列,所以可以生成多个线程(每个系列两个线程,一个用于数据集更新,一个用于图形更新)。

我不想创建多个进度条。相反,我想要一个单独的进度条来通知全局进度。目前我能想到的是MVC和Observer/Observable,但它还有点模糊:)也许有人可以指点我正确的方向,谢谢。

4个回答

6

我曾经花费了一整个星期的时间,试图制作一个平滑、无卡顿的进度条,以适应非常复杂的算法。

这个算法有6个不同的步骤。每个步骤都有严重依赖于A)正在处理的底层数据的时间特征,不仅仅是数据的“数量”,还包括数据的“类型”和B)其中2个步骤随着CPU数量的增加而极大地扩展,2个步骤在2个线程中运行,而2个步骤则有效地只能单线程运行。

数据的混合对每个步骤的执行时间影响要比核心数更大。

最终解决方案非常简单。我创建了6个函数,分析数据集并尝试预测每个分析步骤的实际运行时间。每个函数的启发式分析了正在分析的数据集和CPU数量。根据我的4核机器上的运行时间数据,每个函数基本上返回了它预计需要花费的毫秒数,在我的机器上。

f1(..) + f2(..) + f3(..) + f4(..) + f5(..) + f6(..) = 总运行时间(毫秒)

现在有了这些信息,你就可以有效地知道每个步骤应该占总执行时间的百分比。如果你说step1应该占40%的执行时间,你基本上需要找出如何从该算法中发出40个1%的事件。比如说,如果for循环正在处理100,000个项目,你可能可以这样做:

for (int i = 0; i < numItems; i++){
     if (i % (numItems / percentageOfTotalForThisStep) == 0) emitProgressEvent();
     .. do the actual processing ..
}

这个算法给我们带来了一个顺滑流畅、表现完美的进度条。你的实现技术可以在进度条中具有不同形式的缩放和功能,但解决问题的基本思路是相同的。

而且,这并不真的重要,启发式参考数字是在我的机器上计算出来的——唯一真正的问题是如果你想在不同的机器上运行时更改数字。但你仍然知道比率(这是唯一真正重要的事情),所以你可以看到你的本地硬件与我使用的硬件运行的差异。

现在,普通的SO读者可能会想知道为什么有人会花费一周时间制作一个平滑的进度条。这个功能是销售主管提出的要求,我相信他在销售会议中使用它来获得合同。金钱万能 ;)


2
在涉及到线程或异步进程/任务的情况下,我发现在主线程中拥有一个抽象类型或对象来代表(并理想地封装)每个进程是很有帮助的。因此,对于每个工作线程,主线程中可能会有一个对象(让我们称其为Operation)来管理该工作线程,显然会有一些类似列表的数据结构来保存这些操作。
在适用的情况下,每个操作都提供其工作线程的开始/停止方法,并且在某些情况下 - 例如您的情况 - 表示该特定操作任务的进度和预期总时间或工作的数字属性。如果您知道将执行6,230个计算,则单位不一定需要基于时间,您可以将这些属性视为计数。此外,每个任务都需要以适当的机制(回调、闭包、事件分派或您的编程语言/线程框架提供的任何机制)更新其所属的操作当前进度。
因此,虽然实际工作是在单独的线程中执行的,但“主”线程中的相应操作对象正在持续更新/通知其工作线程的进度。进度条可以相应地更新自己,将操作的“预期”时间总和映射到其总和,并将操作的“进度”时间总和映射到其当前进度,以您的进度条框架的任何合理方式。
显然,在实际实现中还需要考虑和完成大量其他工作,但我希望这能让您有所了解。

1

多个进度条并不是一个坏主意,要知道。或者也可以使用复杂的进度条来显示多个线程运行的情况(就像下载管理程序有时候会有的)。只要用户界面直观易懂,你的用户就会欣赏这些额外的数据。

当我尝试回答这样的设计问题时,我首先会尝试查看其他应用程序中类似或类比的问题以及它们是如何解决的。因此,我建议您通过考虑其他显示复杂进度的应用程序(例如下载管理器示例)并尝试将现有解决方案适应到您的应用程序中来进行一些研究。

很抱歉我不能提供更具体的设计,这只是一般性的建议。 :)


1

对于这种情况,请使用观察者/可观察对象。一些对象观察各个系列处理线程并通过更新摘要栏来报告状态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接