在长时间运行的任务期间保持GUI的响应速度

时间:2020-03-06 14:52:38  来源:igfitidea点击:

在应用程序执行大量CPU处理时保持GUI响应是有效的GUI编程的挑战之一。

这是有关如何在wxPython中执行此操作的很好的讨论。总结起来,有3种方法:

  • 使用线程
  • 使用wxYield
  • 将工作分块并在IDLE事件处理程序中完成

我们发现哪种方法最有效?也欢迎其他框架(例如Qt,GTK或者Windows API)中的技术。

解决方案

线程。它们是我一直追求的目标,因为我们可以在所需的每个框架中进行操作。

而且一旦习惯了一种语言/框架中的多线程和并行处理,我们就可以在所有框架上使用。

为Win32使用Qt / C ++。

我们将主要工作单元划分为不同的流程。 GUI作为独立的进程运行,并且能够根据需要从"工人"进程中命令/接收数据。在当今的多核世界中运行良好。

线程或者进程取决于应用程序。有时,实际上最好让GUI是它自己的程序,并在有工作要做时只将异步调用发送到其他程序。我们最终仍将在GUI中拥有多个线程来监视结果,但是如果所做的工作很复杂且未直接连接到GUI,则可以简化操作。

这个答案不适用于OP关于Python的问题,而更多是元响应。

简单的方法是线程。但是,并非每个平台都有抢占式线程(例如BREW,某些其他嵌入式系统)。如果可能的话,只需对工作进行分块,然后在IDLE事件处理程序中执行即可。

在BREW中使用线程的另一个问题是,它不会清理C ++堆栈对象,因此,如果我们只是杀死线程,那么泄漏内存就太容易了。

我使用线程,因此GUI的主事件循环永远不会阻塞。

对于某些类型的操作,使用单独的过程非常有意义。过去,产生一个进程会产生很多开销。使用现代硬件,这种开销几乎不会在屏幕上一闪而过。如果我们要生成一个长时间运行的进程,则尤其如此。

一个(可争论的)优点是,与线程相比,它是一种更简单的概念模型,它可能导致更易于维护的代码。它还可以使代码更易于测试,因为我们可以编写用于执行这些外部过程的测试脚本,而不必使用GUI。有人甚至可能认为这是主要优势。

对于我曾经处理过的某些代码,从线程切换到单独的进程导致净减少了5000行以上的代码,同时使GUI更具响应性,代码更易于维护和测试,同时不断改进总的整体表现。

绝对是线程。为什么?未来是多核的。几乎所有新CPU都有一个以上的内核,或者如果只有一个,它可能支持超线程,因此假装它有多个。为了有效利用多核CPU(英特尔计划在不远的将来增加到32个核),我们需要多个线程。如果我们在一个主线程中运行所有线程(通常是UI线程是主线程),则用户将拥有8、16和一天32个内核的CPU,并且应用程序永远不会使用多个内核,IOW运行速度要慢得多超出了它的运行能力。

实际上,如果我们现在计划一个应用程序,那么我将放弃经典设计,而想到主/从关系。用户界面是主界面,唯一的任务就是与用户进行交互。那就是向用户显示数据并收集用户输入。每当应用程序需要"处理任何数据"(即使是少量甚至更重要的大数据),创建任何类型的"任务",将此任务转发到后台线程并让该线程执行该任务时,向UI(例如,已完成的百分比或者任务是否仍在运行,因此UI可以显示"工作进度指示器")。如果可能,将任务拆分为许多小的独立子任务,并运行多个后台进程,并为每个子任务提供一个子任务。这样,应用程序才能真正从多核​​中受益,并且CPU越多,速度就越快。

实际上,像Apple和Microsoft这样的公司已经在计划如何使自己最单线程的UI自己成为多线程。即使采用上述方法,也有一天我们可能会遇到UI本身就是瓶颈的情况。后台进程可以比UI向用户呈现或者要求用户输入的数据处理速度快得多。如今,许多UI框架几乎没有线程安全,很多根本没有线程安全,但是这种情况将会改变。串行处理(一个接一个地执行任务)是一项垂死的设计,而并行处理(一次执行多个任务)则是未来的发展方向。只需看一下图形适配器即可。如果仅查看GPU的MHz / GHz处理速度,即使是最现代的NVidia卡也具有可怜的性能。在进行3D计算时,如何击败CPU?简单:它无需并行计算一个多边形点或者一个纹理像素,而是并行计算许多多边形点(实际上是同时计算一整堆),这样一来,其吞吐量仍然使CPU哭泣。例如。 ATI X1900(也叫竞争对手)有48个着色器单元!

线程数
让我们使用一个简单的2层视图(GUI,应用程序逻辑)。

应用程序逻辑工作应在单独的Python线程中完成。对于需要传播到GUI层的异步事件,请使用wx的事件系统发布自定义事件。张贴wx事件是线程安全的,因此可以想象我们可以在多个上下文中进行。

在另一个方向上工作(GUI输入事件触发应用程序逻辑),我发现最好是对定制事件系统进行滚动部署。使用"队列"模块具有推入和弹出事件对象的线程安全方式。然后,对于每个同步成员函数,将其与异步版本配对,以将同步函数对象和参数推送到事件队列中。

如果一次只能执行一个应用程序逻辑级别的操作,则此方法特别有用。该模型的好处在于,同步很简单,每个同步功能从头到尾在其自己的上下文中按顺序工作,而不必担心先占或者手工编码产生。我们将不需要锁来保护关键部分。在该功能的末尾,将一个事件发布到GUI层,指示该操作已完成。

我们可以对其进行扩展,以允许存在多个应用程序级线程,但是与同步有关的常见问题会再次出现。

编辑忘记提及它的好处是可以将应用程序逻辑与GUI代码完全分离。如果我们决定使用其他框架或者使用提供应用程序的命令行版本,则模块化将有所帮助。为此,我们将需要一个由GUI层实现的中间事件分派器(应用程序级别-> GUI)。

我认为delayedresult是我们要寻找的:

http://www.wxpython.org/docs/api/wx.lib.delayedresult-module.html

有关示例,请参见wxpython演示。