QT 中的线程支持
Qt以独立于平台的线程类、发布事件的线程安全方式以及跨线程的信号槽连接的形式提供线程支持。这使得开发可移植的多线程Qt应用程序变得容易,并利用多处理器机器。多线程编程对于执行耗时的操作而不冻结应用程序的用户界面也是一种有用的范例。
线程类
类 | 描述 |
---|---|
QAtomicInteger | 整数上独立于平台的原子操作 |
QAtomicPointer | 对指针提供独立于平台的原子操作的模板类 |
QFuture | 表示异步计算的结果 |
QFutureSynchronizer | 简化QFuture同步的便利类 |
QFutureWatcher | 允许使用信号和插槽监视QFuture |
QMutex | 线程之间的访问序列化 |
QMutexLocker | 简化互斥锁锁定和解锁的方便的类 |
QReadLocker | 简化了读写锁的锁定和解锁以实现读访问的方便类 |
QReadWriteLock | 读写锁 |
QRecursiveMutex | 线程之间的访问序列化 |
QRunnable | 所有可运行对象的基类 |
QSemaphore | 通用计数信号量 |
QSemaphoreReleaser | QSemaphore::release()调用的异常安全延迟 |
QThread | 独立于平台的线程管理方式 |
QThreadPool | 管理QThread集合 |
QThreadStorage | 每线程数据存储 |
QWaitCondition | 用于同步线程的条件变量 |
QWriteLocker | 简化了对写访问的读写锁的锁定和解锁方便类 |
QtConcurrent | 高级API,可以在不使用低级线程原语的情况下编写多线程程序 |
Qt的线程类是用本机线程API实现的;例如Win32和pthreads。因此,它们可以与相同本机API的线程一起使用。
QT中的多线程技术
Qt提供了许多用于处理线程的类和函数。以下是Qt程序员可以用来实现多线程应用程序的四种不同方法。
QThread:具有可选事件循环的低级别API
QThread是Qt中所有线程控制的基础。每个QThread实例表示并控制一个线程。
QThread既可以直接实例化,也可以子类化。实例化QThread提供了一个并行事件循环,允许在辅助线程中调用QObject槽。通过对QThread进行子类化,应用程序可以在开始其事件循环之前初始化新线程,或者在没有事件循环的情况下运行并行代码。
QThreadPool和QRunnable:重用线程
频繁地创建和销毁线程可能代价高昂。为了减少这种开销,可以将现有线程重新用于新任务。QThreadPool是可重复使用的QThread的集合。
要在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可以通过信号和插槽与QFutures进行交互。
Qt Concurrent的map、filter和reduce算法会自动将计算分布在所有可用的处理器核心上,因此今天编写的应用程序在稍后部署到具有更多核心的系统上时将继续扩展。
该模块还提供了QtConcurrent::run()函数,它可以在另一个线程中运行任何函数。但是,QtConcurrent::run()只支持map、filter和reduce函数可用的一个子集功能。QFuture可用于检索函数的返回值并检查线程是否正在运行。但是,对QtConcurrent::run()的调用只使用一个线程,不能暂停/恢复/取消,也不能查询进度。
WorkerScript:QML中的线程
WorkerScript QML类型允许JavaScript代码与GUI线程并行运行。
每个WorkerScript实例都可以附加一个.js脚本。当调用WorkerScript.sendMessage()时,该脚本将在单独的线程(和单独的QML上下文)中运行。当脚本完成运行时,它可以向GUI线程发送一个回复,GUI线程将调用WorkerScript.onMessage()信号处理程序。
使用WorkerScript类似于使用已移动到另一个线程的worker QObject。数据通过信号在线程之间传输。
选择合适的方法
如上所述,Qt为开发线程应用程序提供了不同的解决方案。给定应用程序的正确解决方案取决于新线程的用途和线程的使用寿命。下面是Qt的线程技术的比较,然后是一些示例用例的推荐解决方案。