在应用程序执行一些 CPU 密集型处理时保持 GUI 响应是有效 GUI 编程的挑战之一。
这是关于如何在 wxPython 中执行此操作的一个很好的讨论。总结一下,有3种方式:
- 使用线程
- 使用 wxYield
- 分块工作并在 IDLE 事件处理程序中完成
您发现哪种方法最有效?来自其他框架(如 Qt、GTK 或 Windows API)的技术也受到欢迎。
在应用程序执行一些 CPU 密集型处理时保持 GUI 响应是有效 GUI 编程的挑战之一。
这是关于如何在 wxPython 中执行此操作的一个很好的讨论。总结一下,有3种方式:
您发现哪种方法最有效?来自其他框架(如 Qt、GTK 或 Windows API)的技术也受到欢迎。
线程。它们是我一直追求的,因为你可以在你需要的每个框架中做到这一点。
一旦你习惯了一种语言/框架中的多线程和并行处理,你就会在所有框架上都很好。
绝对是线程。为什么?未来是多核的。几乎任何新的 CPU 都有多个内核,或者如果它只有一个内核,它可能支持超线程,从而假装它有多个内核。为了有效地利用多核 CPU(英特尔计划在不久的将来达到 32 核),您需要多个线程。如果你在一个主线程中运行所有内容(通常 UI 线程是主线程),用户将拥有 8、16 和一天 32 核的 CPU,而你的应用程序永远不会使用超过一个,IOW 运行速度要慢得多比它可以运行。
实际上,如果您现在计划应用程序,我会摆脱经典设计并考虑主/从关系。你的 UI 是主人,它唯一的任务就是与用户交互。那就是向用户显示数据并收集用户输入。每当您的应用程序需要“处理任何数据”(即使是少量数据和更重要的大数据)时,创建任何类型的“任务”,将此任务转发到后台线程并让线程执行任务,向后台提供反馈UI(例如,它已经完成了多少百分比,或者任务是否仍在运行,因此 UI 可以显示“正在进行的工作指示器”)。如果可能,将任务拆分为许多小的、独立的子任务,并运行多个后台进程,为每个子任务提供一个子任务。
实际上,像苹果和微软这样的公司已经在计划如何让他们仍然最单线程的 UI 本身成为多线程的。即使采用上述方法,您也可能有一天会遇到 UI 本身就是瓶颈的情况。后台进程处理数据的速度比 UI 将数据呈现给用户或要求用户输入的速度要快得多。今天,许多 UI 框架都很少是线程安全的,许多根本不是线程安全的,但这将会改变。串行处理(一个接一个地完成一个任务)是一种垂死的设计,并行处理(一次完成许多任务)是未来的发展方向。看看图形适配器。如果仅查看 GPU 的处理速度(以 MHz/GHz 为单位),即使是最现代的 NVidia 卡的性能也很差。在 3D 计算方面,它怎么能打败 CPU?简单的:它不是一个接一个地计算一个多边形点或一个纹理像素,而是并行计算其中的许多(实际上是同时计算一大堆),这样它就达到了仍然让 CPU 哭泣的吞吐量。例如,ATI X1900(也是竞争对手的名字)有 48 个着色器单元!
我认为delayedresult
这是您正在寻找的:
http://www.wxpython.org/docs/api/wx.lib.delayedresult-module.html
有关示例,请参见 wxpython 演示。
线程或进程取决于应用程序。有时最好让 GUI 成为它自己的程序,并在它有工作要做时向其他程序发送异步调用。您最终仍将在 GUI 中使用多个线程来监视结果,但如果正在完成的工作很复杂并且没有直接连接到 GUI,它可以简化事情。
线程 - 让我们使用一个简单的 2 层视图(GUI、应用程序逻辑)。
应用程序逻辑工作应该在一个单独的 Python 线程中完成。对于需要向上传播到 GUI 层的异步事件,使用 wx 的事件系统来发布自定义事件。发布 wx 事件是线程安全的,因此您可以想象从多个上下文中执行此操作。
在另一个方向工作(触发应用程序逻辑的 GUI 输入事件),我发现最好自制一个自定义事件系统。使用 Queue 模块以线程安全的方式推送和弹出事件对象。然后,对于每个同步成员函数,将其与将同步函数对象和参数推送到事件队列的异步版本配对。
如果一次只能执行一个应用程序逻辑级操作,则此方法特别有效。这个模型的好处是同步很简单——每个同步函数在它自己的上下文中从头到尾按顺序工作,而不用担心抢占或手工编码的让步。您不需要锁来保护您的关键部分。在函数结束时,向 GUI 层发布一个事件,指示操作已完成。
您可以扩展它以允许存在多个应用程序级线程,但通常会重新出现同步问题。
编辑- 忘了提到它的美妙之处在于可以将应用程序逻辑与 GUI 代码完全分离。如果您决定使用不同的框架或使用提供应用程序的命令行版本,模块化会有所帮助。为此,您将需要一个由 GUI 层实现的中间事件调度程序(应用程序级别 -> GUI)。
使用 Qt/C++ for Win32。
我们将主要工作单元划分为不同的流程。GUI 作为一个单独的进程运行,并且能够根据需要从“工作”进程命令/接收数据。在当今的多核世界中运行良好。
此答案不适用于 OP 关于 Python 的问题,而更多的是元响应。
简单的方法是线程。然而,并不是每个平台都有抢占式线程(例如 BREW,一些其他嵌入式系统)。如果可能,只需将工作分块并在 IDLE 事件处理程序中完成。
在 BREW 中使用线程的另一个问题是它不会清理 C++ 堆栈对象,因此如果您简单地终止线程,就很容易泄漏内存。
我使用线程,所以 GUI 的主事件循环永远不会阻塞。
对于某些类型的操作,使用单独的进程很有意义。过去,生成一个进程会产生大量开销。使用现代硬件,这种开销在屏幕上几乎不是一个亮点。如果您正在生成一个长时间运行的进程,则尤其如此。
一个(有争议的)优点是它是一个比线程更简单的概念模型,可能会导致更可维护的代码。它还可以使您的代码更易于测试,因为您可以编写执行这些外部过程的测试脚本,而无需涉及 GUI。有些人甚至可能认为这是主要优势。
对于我曾经编写过的一些代码,从线程切换到单独的进程导致净减少超过 5000 行代码,同时使 GUI 响应更快,代码更易于维护和测试,同时改进整体的整体表现。