Qt 中的多线程技术
Qt 提供了许多用于处理线程的类和函数。以下是 Qt 程序员可用于实现多线程应用程序的四种不同方法。
QThread:带有可选事件循环的低级 API
QThread 是 Qt 中所有线程控制的基础。每个 实例代表并控制一个线程。QThread
QThread QThread 可以直接实例化,也可以子类化。实例化 可提供并行事件循环,允许在辅助线程中调用 插槽。子类化 允许应用程序在启动事件循环前初始化新线程,或在没有事件循环的情况下运行并行代码。QThread QObject QThread
有关如何使用QThread 的演示,请参见QThread class reference 和线程示例。
QThreadPool 和 QRunnable:重用线程
频繁创建和销毁线程的成本很高。为了减少这种开销,可将现有线程重用于新任务。QThreadPool 是一个可重用的 QThreads 集合。
要在QThreadPool 的线程中运行代码,可重新实现QRunnable::run() 并实例化子类QRunnable 。使用QThreadPool::start() 将QRunnable 置于QThreadPool 的运行队列中。当线程可用时,QRunnable::run() 中的代码将在该线程中执行。
每个 Qt XML 应用程序都有一个全局线程池,可通过QThreadPool::globalInstance() 访问。全局线程池会根据 CPU 的内核数自动维护最佳线程数。不过,也可以明确创建和管理单独的QThreadPool 。
Qt Concurrent:使用高级应用程序接口
该 Qt Concurrent模块提供了处理一些常见并行计算模式的高级函数:map、filter 和 reduce。与使用QThread 和QRunnable 不同,这些函数从不需要使用互斥或信号等低级线程原语。相反,这些函数会返回一个QFuture 对象,当函数准备就绪时,可使用该对象检索函数结果。QFuture 还可用于查询计算进度,以及暂停/继续/取消计算。为方便起见,QFutureWatcher 可通过信号和插槽与QFuture进行交互。
Qt Concurrent该模块的映射、过滤和还原算法会自动将计算分配给所有可用的处理器内核,因此现在编写的应用程序以后部署到内核更多的系统上时仍可继续扩展。
该模块还提供了QtConcurrent::run() 函数,可以在另一个线程中运行任何函数。不过,QtConcurrent::run() 只支持 map、filter 和 reduce 函数可用的部分功能。QFuture 可用于获取函数的返回值,并检查线程是否正在运行。不过,调用QtConcurrent::run() 只能使用一个线程,不能暂停/恢复/取消,也不能查询进度。
请参阅 Qt Concurrent模块文档,了解各个函数的详细信息。
WorkerScript:QML 中的线程
WorkerScript QML 类型可让 JavaScript 代码与 GUI 线程并行运行。
每个WorkerScript 实例可附加一个.js
脚本。调用WorkerScript.sendMessage() 时,脚本将在单独的线程(和单独的QML context )中运行。脚本运行结束后,可以向 GUI 线程发送回复,GUI 线程将调用WorkerScript.onMessage() 信号处理程序。
使用WorkerScript 类似于使用已转移到另一个线程的 WorkerQObject 。数据是通过信号在线程间传输的。
有关如何实现脚本的详细信息,以及可在线程间传递的数据类型列表,请参阅WorkerScript 文档。
选择合适的方法
如上所述,Qt 为开发线程应用程序提供了不同的解决方案。特定应用程序的正确解决方案取决于新线程的目的和线程的生命周期。下面是 Qt 线程技术的比较,以及针对一些示例用例推荐的解决方案。
解决方案比较
特性 | QThread | QRunnable 和QThreadPool | QtConcurrent::run() | Qt Concurrent (映射、过滤、还原) | WorkerScript |
---|---|---|---|---|---|
语言 | C++ | C++ | C++ | C++ | QML |
可指定线程优先级 | 可以 | 可以 | |||
线程可运行事件循环 | 是 | ||||
线程可通过信号接收数据更新 | 是(由 Worker 接收QObject) | 是(由WorkerScript 接收) | |||
线程可以使用信号控制 | 是(由QThread 接收 ) | 是(由QFutureWatcher 接收 ) | |||
线程可通过QFuture | 部分 | 是 | |||
内置暂停/继续/取消功能 | 是 |
示例用例
线程寿命 | 操作 | 解决方案 |
---|---|---|
一次调用 | 在另一个线程中运行一个新的线性函数,运行过程中可选择更新进度。 | Qt 提供了不同的解决方案:
|
一次调用 | 在另一个线程中运行现有函数并获取其返回值。 | 使用QtConcurrent::run() 运行函数。当函数返回时,让QFutureWatcher 发出finished() 信号,并调用QFutureWatcher::result() 获取函数的返回值。 |
一次调用 | 使用所有可用内核对容器中的所有项目执行操作。例如,从图片列表中生成缩略图。 | 使用Qt Concurrent 的QtConcurrent::filter() 函数选择容器元素,并使用QtConcurrent::map() 函数对每个元素执行操作。要将输出折叠成一个结果,请使用QtConcurrent::filteredReduced() 和QtConcurrent::mappedReduced() 代替。 |
一次调用/永久 | 在纯 QML 应用程序中执行长时间计算,并在结果就绪时更新图形用户界面。 | 将计算代码放在.js 脚本中,并将其附加到WorkerScript 实例。调用WorkerScript.sendMessage() 在新线程中启动计算。让脚本也调用 sendMessage() 将结果传回 GUI 线程。在onMessage 中处理结果并更新图形用户界面。 |
永久性 | 在另一个线程中存在一个对象,该对象可以根据请求执行不同的任务和/或接收新数据。 | 子类化QObject 以创建一个 Worker。实例化该 Worker 对象和QThread 。将 Worker 移至新线程。通过队列信号槽连接向 Worker 对象发送命令或数据。 |
永久 | 在另一个线程中重复执行昂贵的操作,该线程无需接收任何信号或事件。 | 直接在QThread::run() 的重新实现中编写无限循环。在没有事件循环的情况下启动线程。让线程发出信号,将数据发送回 GUI 线程。 |
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.