原文链接
Multithreading Technologies in Qtdoc.qt.io正文
Qt 提供一系列的类与函数来处理多线程。Qt 开发者们可以使用下面四种方法来实现多线程应用。
QThread: 底层 API 与可选的事件循环
作为 Qt 进行线程控制的基石,每一个 QThread 实例都代表并控制着一个线程。
您可以直接实例化 QThread,或建立子类。实例化一个 QThread 将附带一个并行事件循环,允许 QObject 槽函数在子线程执行。若子类化一个 QThread,程序可以在事件循环启动前初始化这个新线程;或者在无事件循环下运行并行代码。
另请参阅: QThread 类文档 以及示例代码 多线程范例 来了解如何使用 QThread。
QThreadPool 与 QRunnable: 线程重用
如果频繁地创建与销毁线程,资源开销将会非常大。为了减少这样额外的开销,可以重复使用一些现成的线程来执行新的任务。QThreadPool 就是这样一个保存着可重用的 QThead 的集合。
为了将代码放入 QThreadPool 的线程中运行,可以重写 QRunnable::run() 函数并实例化继承自 QRunnable 的子类。调用 QThreadPool::start() 函数可将 QRunnable 添加到 QThreadPool 的运行队列。一旦出现了一个可用的线程,它将会执行 QRunnable::run() 里的代码。
每一个 Qt 程序都会自带一个公共线程池,可以通过调用 QThreadPool::globalInstance() 来获取。公共线程池会自动维持着一定数量的线程,线程数为基于 CPU 核心数计算的最佳值。不过,您也可以显式创建并管理一个独立的 QThreadPool 。
Qt Concurrent: 使用高层 API
Qt Concurrent 模块提供了数个高级函数,用于处理一些常见的并行计算模式:map、filter 和 reduce。不同于使用 QThread 与 QRunnable,这些高级函数不需要使用底层线程原语,比如互斥锁与信号量。取而代之的是返回一个 QFuture 对象,它能够在传入的函数返回值就绪后检索该结果。QFuture 既可以用来查询计算进度,也可以暂停/恢复/取消计算。方便起见,QFutureWatcher 可以让您通过信号槽与 QFuture 进行交互。
Qt Concurrent 的 map、filter 和 reduce 算法会自动将计算过程分配到可用的处理器核心,由此,当下编写的程序在以后部署到更多核心的系统上时会被自动扩展。
此模块还提供了 QtConcurrent::run() 函数,可以将任何函数在另一个线程中运行。不过,QtConcurrent::run() 仅提供 map 、 filter 和 reduce 函数的一部分功能。QFuture 可以用于检索函数返回值,也可以用于查看线程是否处于运行中。然而,调用 QtConcurrent::run() 时只会使用一个线程,并且无法暂停/恢复/取消,也不能查询计算进度。
另请参阅: Qt Concurrent 模块文档以获取各个函数的详细信息。
WorkerScript: QML中的多线程
QML 类型 WorkerScript 可将 JavaScript 代码与 GUI 线程并行运行。
每个 WorkerScript 实例可附加一个 .js
脚本。当调用 WorkerScript.sendMessage() 时,脚本将会运行在一个独立的线程中(伴随一个独立的 QML 上下文)。在脚本运行结束后,WorkerScript 将会向 GUI 线程发送回复,后者会调用 WorkerScript.onMessage() 信号处理函数。
使用 WorkerScript,很像使用一个移入子线程工作的 QObject,数据通过信号槽在线程间进行传输。
另请参阅:WorkerScript 文档以获得实现脚本的详细信息,以及能够在线程间传输的数据类型列表。
选择合适的方法
如上文所述,Qt 提供了开发多线程应用的不同解决方案。对一个给定的程序,需要根据新线程的用途与线程的生命周期来决定正确的方案。下面是一组 Qt 多线程技术的功能对比表,以及对于一些范例较为推荐的解决方案。