当主线程忙碌时如何使Qt工作?

30

我的程序的主线程(函数main在其中)保留用于非GUI任务。它调用了许多耗时的计算函数。所有已实现的GUI都在单独的线程中完成其工作。

我现在将使用Qt实现另一个GUI。Qt文档表示所有与GUI相关的任务都应在主线程中完成。 在我的情况下,在主线程中插入偶尔的QCoreApplication::processEvents()调用几乎是无用的,因为它们之间存在很大的延迟。

有没有办法克服Qt的这个限制? 在Qt程序的主线程中是否不可能执行任何与非GUI相关的操作?


4
为什么你不能在另一个线程上完成你的工作? - Kornel
3
仅出于历史原因。重构将会很麻烦,所以我考虑进行一些研究,看是否可以避免。 - Basilevs
4个回答

33
不,你应该在一个单独的线程中进行计算。正如你已经提到的,使用QCoreApplication::processEvents()有一种可行的解决方法,但听起来你无法让它为你工作。
如果你不想费力地设置 QThread 并移动所有代码,你可能会发现QtConcurrent::run函数很有用,它允许你异步运行一个函数。
几个要点: 你应该尽量保持主(GUI)线程尽量轻量级。大量IO或计算应该使用QtConcurrent::run异步地执行或者在一个单独的QThread中执行。根据你的代码复杂度,你可能只需要使用QtConcurrent方法就能完成。

1
我没有看到使用QtConcurrent::run()相比于QThread的显著优势。实际上,这样做更费力。坚持使用QThread,它要简单得多实现。 - yan bellavance
3
这取决于你在做什么以及你的程序结构。如果你需要一个必须一直运行的永久线程,则使用QThread。如果你需要许多小的(通常是独立的)任务运行,并且你不关心它们在何时运行,那么请使用QtConcurrent。 - Thomi
是的,我同意我可能对它有点苛刻……就像你说的,这取决于你的需求。 - yan bellavance
不同意。只需创建一个 QThread 并在其上托管 QObject!毕竟,通过信号和槽将托管在 main() 上的 GUI 代码和托管在 QThread 上的 QObject 连接起来。通过简单的事件驱动方法消除为每个任务创建线程的开销。 - Brian Cannard

7
最好将长时间的计算放到其他线程中,以便主GUI线程保持响应。 旧式的单处理方式是确保您的计算不会在没有轮询GUI事件处理程序的情况下运行太长时间,但这无法扩展到多核心。
幸运的是,Qt具有出色的线程支持。 过去,您必须为例如使用QThreadQMutexQWaitCondition等将任务委派给线程池而自己编写系统,但是最近的Qt版本通过更高级别的抽象(如QThreadPoolQtConcurrent :: run QFuture)使事情变得更加容易。

0

在Qt的文档中,"main thread" 的概念并没有明确定义。实际上,一个进程的主线程(执行 Process.run 函数的进程)可能与Qt的主线程(首个实例化的Qt对象,如 QApplication)不同,尽管这两个 "main" 线程通常是相同的。

以下是一个有效代码结构的示例:

下面的函数将在进程的非主线程 'thread-1' 中运行,并且会立即成为Qt的主线程。

def startThread1():      
    app = QApplication(sys.argv)
    app.exec_()  # enter event loop

以下代码在进程的主线程中运行,不要与进程的主Qt和唯一GUI线程混淆。

thread1 = Thread(target=self.startThread1)
thread1.start()
input('I am busy until you press enter')

0

如果你从另一个线程调用 QApplication::exec(),然后它变成了你的 GUI 线程,我不确定事情会如何进行。只是一个想法。

(如果有效,请告诉我们,这将很有趣...)


你为什么想要这样做呢?如果你需要将代码分割并在不同的线程上运行一半,那么你最好做到完全正确...或者我有什么遗漏吗? - Thomi
我并不是在推荐,只是提供一个方法来精确地实现OP所要求的内容。原因可能是其他需要在主线程中完成的库。(如果我没记错的话,OpenSG 1.x需要在同一线程中进行渲染和加载,所以我们最终在主线程中进行了大量的OpenGL绘制(因为我们在该线程中加载了所有图形)。在那里运行GUI也不好玩,但那就是它的运行方式。(我记得在2.x中已经修复了这个问题)。 - Macke
1
你可以做到,我们只是用一个简单的应用程序做到了。但是当我们的程序关闭时,我们会得到一个无效的free()错误或双重free()错误。 - dgrant
@dgrant:太酷了!那么在适当重构之前,这可能作为临时解决方法。可以通过终止进程来解决free()错误。当然,再次强调,不建议这样做,但由于Qt和.NET之间的不兼容性至少有一次我们被迫这样做。 - Macke
这个方法适用于Windows和Linux:如何避免Qt app.exec()阻塞主线程 - Basilevs

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