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

发布于 2024-07-13 11:55:22 字数 312 浏览 5 评论 0原文

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

我的应用程序处理大量数据并生成一些图表。 数据处理(从文件中获取、CPU 密集型计算)和图形操作(绘图、更新)在单独的线程中完成。

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

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

I'm looking for a design pattern that would fit my application design.

My application processes large amounts of data and produces some graphs.
Data processing (fetching from files, CPU intensive calculations) and graph operations (drawing, updating) are done in seperate threads.

Graph can be scrolled - in this case new data portions need to be processed.
Because there can be several series on a graph, multiple threads can be spawned (two threads per serie, one for dataset update and one for graph update).

I don't want to create multiple progress bars. Instead, I'd like to have single progress bar that inform about global progress. At the moment I can think of MVC and Observer/Observable, but it's a little bit blurry :) Maybe somebody could point me in a right direction, thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

月光色 2024-07-20 11:55:22

我曾经花了一周的大部分时间试图在一个非常复杂的算法上制作一个平滑、不打嗝的进度条。

该算法有 6 个不同的步骤。 每个步骤都有时序特征,这些特征严重依赖于 A) 正在处理的底层数据,不仅是数据的“数量”,还包括数据的“类型”和 B) 随着 cpu 数量的增加,其中 2 个步骤的扩展性非常好, 2 个步骤在 2 个线程中运行,2 个步骤实际上是单线程的。

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

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

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

现在有了这些信息,您可以有效地了解什么每个步骤应该花费的总执行时间的百分比。 现在,如果您说步骤 1 应该占用 40% 的执行时间,那么您基本上需要找出如何从该算法发出 40 个 1% 事件。 假设 for 循环正在处理 100,000 个项目,您可能会这样做:

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

该算法为我们提供了一个执行完美的丝滑进度条。 您的实现技术可以在进度条中具有不同形式的缩放和可用功能,但思考问题的基本方式是相同的。

是的,启发式参考数字是在我的机器上计算出来的并不重要 - 唯一真正的问题是如果您想在另一台机器上运行时更改数字。 但您仍然知道该比率(这是这里唯一真正重要的事情),因此您可以看到您的本地硬件的运行方式与我的硬件有何不同。

现在普通的 SO 读者可能想知道为什么有人会花一周的时间制作一个平滑的进度条。 该功能是由首席销售员要求的,我相信他在销售会议中使用它来获得合同。 有钱能使鬼推磨 ;)

I once spent the best part of a week trying to make a smooth, non-hiccupy progress bar over a very complex algorithm.

The algorithm had 6 different steps. Each step had timing characteristics that were seriously dependent on A) the underlying data being processed, not just the "amount" of data but also the "type" of data and B) 2 of the steps scaled extremely well with increasing number of cpus, 2 steps ran in 2 threads and 2 steps were effectively single-threaded.

The mix of data effectively had a much larger impact on execution time of each step than number of cores.

The solution that finally cracked it was really quite simple. I made 6 functions that analyzed the data set and tried to predict the actual run-time of each analysis step. The heuristic in each function analyzed both the data sets under analysis and the number of cpus. Based on run-time data from my own 4 core machine, each function basically returned the number of milliseconds it was expected to take, on my machine.

f1(..) + f2(..) + f3(..) + f4(..) + f5(..) + f6(..) = total runtime in milliseconds

Now given this information, you can effectively know what percentage of the total execution time each step is supposed to take. Now if you say step1 is supposed to take 40% of the execution time, you basically need to find out how to emit 40 1% events from that algorithm. Say the for-loop is processing 100,000 items, you could probably do:

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

This algorithm gave us a silky smooth progress bar that performed flawlessly. Your implementation technology can have different forms of scaling and features available in the progress bar, but the basic way of thinking about the problem is the same.

And yes, it did not really matter that the heuristic reference numbers were worked out on my machine - the only real problem is if you want to change the numbers when running on a different machine. But you still know the ratio (which is the only really important thing here), so you can see how your local hardware runs differently from the one I had.

Now the average SO reader may wonder why on earth someone would spend a week making a smooth progress bar. The feature was requested by the head salesman, and I believe he used it in sales meetings to get contracts. Money talks ;)

一张白纸 2024-07-20 11:55:22

在像这样的线程或异步进程/任务的情况下,我发现在主线程中拥有一个代表(并且最好封装)每个进程的抽象类型或对象很有帮助。 因此,对于每个工作线程,主线程中可能会有一个对象(我们称之为Operation)来管理该工作线程,并且显然会有某种类似列表的数据结构来保存这些操作。

在适用的情况下,每个操作为其工作人员提供启动/停止方法,并且在某些情况下(例如您的情况)表示该特定操作任务的进度和预期总时间或工作的数字属性。 这些单位不一定需要基于时间,如果您知道将执行 6,230 次计算,您可以将这些属性视为计算计数。 此外,每个任务都需要有某种方法来以任何适当的机制(回调、闭包、事件分派或编程语言/线程框架提供的任何机制)更新其当前进度的操作。

因此,当您的实际工作在单独的线程中执行时,“主”线程中相应的操作对象会不断更新/通知其工作进程的进度。 进度条可以相应地更新自身,将操作的“预期”时间总数映射到其总数,并将操作的“进度”时间总数映射到其当前进度,以任何对进度条框​​架有意义的方式。

显然,在实际实施过程中还需要做大量其他考虑/工作,但我希望这能让您了解其要点。

In situations with threads or asynchronous processes/tasks like this, I find it helpful to have an abstract type or object in the main thread that represents (and ideally encapsulates) each process. So, for each worker thread, there will presumably be an object (let's call it Operation) in the main thread to manage that worker, and obviously there will be some kind of list-like data structure to hold these Operations.

Where applicable, each Operation provides the start/stop methods for its worker, and in some cases - such as yours - numeric properties representing the progress and expected total time or work of that particular Operation's task. The units don't necessarily need to be time-based, if you know you'll be performing 6,230 calculations, you can just think of these properties as calculation counts. Furthermore, each task will need to have some way of updating its owning Operation of its current progress in whatever mechanism is appropriate (callbacks, closures, event dispatching, or whatever mechanism your programming language/threading framework provides).

So while your actual work is being performed off in separate threads, a corresponding Operation object in the "main" thread is continually being updated/notified of its worker's progress. The progress bar can update itself accordingly, mapping the total of the Operations' "expected" times to its total, and the total of the Operations' "progress" times to its current progress, in whatever way makes sense for your progress bar framework.

Obviously there's a ton of other considerations/work that needs be done in actually implementing this, but I hope this gives you the gist of it.

千纸鹤 2024-07-20 11:55:22

请注意,多个进度条并不是一个坏主意。 或者可能是一个复杂的进度条,显示多个正在运行的线程(就像下载管理器程序有时具有的那样)。 只要用户界面直观,您的用户就会欣赏额外的数据。

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

抱歉,我无法提供更具体的设计,这只是一般建议。 :)

Multiple progress bars aren't such a bad idea, mind you. Or maybe a complex progress bar that shows several threads running (like download manager programs sometimes have). As long as the UI is intuitive, your users will appreciate the extra data.

When I try to answer such design questions I first try to look at similar or analogous problems in other application, and how they're solved. So I would suggest you do some research by considering other applications that display complex progress (like the download manager example) and try to adapt an existing solution to your application.

Sorry I can't offer more specific design, this is just general advice. :)

梦开始←不甜 2024-07-20 11:55:22

对于这种事情,坚持使用 Observer/Observable。 某些对象观察各个系列处理线程并通过更新摘要栏来报告状态。

Stick with Observer/Observable for this kind of thing. Some object observes the various series processing threads and reports status by updating the summary bar.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文