Qt多线程官方教程2——Qt中的多线程技术

16 篇文章 2 订阅

目录

QThread:具有可选事件循环的低级API

QThreadPool和QRunnable:重用线程

Qt Concurrent:使用高级API

WorkerScript:QML中的线程

选择适当的方法


Qt提供了许多用于处理线程的类和函数。 Qt程序员可以使用以下四种不同的方法来实现多线程应用程序。

QThread:具有可选事件循环的低级API

QThread是Qt中所有线程控制的基础。 每个QThread实例代表并控制一个线程。

QThread可以直接实例化也可以子类化。 实例化QThread提供了一个并行事件循环,从而允许在辅助线程中调用QObject槽。 子类化QThread允许应用程序在开始其事件循环之前初始化新线程,或者在没有事件循环的情况下运行并行代码。

有关如何使用QThread的演示,请参见QThread类参考和线程示例

QThreadPool和QRunnable:重用线程

频繁创建和销毁线程开销可能会比较大。为了减少这种开销,可以将现有线程重用于新任务。 QThreadPool是可重用的QThreads的集合。

要在QThreadPool的线程之一中运行代码,请重新实现QRunnable::run()并实例化子类QRunnable。使用QThreadPool :: start()将QRunnable放入QThreadPool的运行队列中。当线程可用时,QRunnable::run()中的代码将在该线程中执行。

每个Qt应用程序都有一个全局线程池,可通过QThreadPool::globalInstance()访问该线程池。该全局线程池根据CPU中的内核数自动维护最佳线程数。但是,可以显式创建和管理单独的QThreadPool。

Qt Concurrent:使用高级API

Qt Concurrent模块提供了高级功能,可处理一些常见的并行计算模式:映射,过滤和归约。

与使用QThread和QRunnable不同,这些函数从不需要使用低级线程原语,例如互斥量或信号量。相反,它们返回一个QFuture对象,当准备就绪时,该对象可用于检索函数的结果。 QFuture还可以用于查询计算进度以及暂停/恢复/取消计算。为方便起见,QFutureWatcher允许通过信号和槽与QFutures进行交互。

Qt Concurrent的映射,过滤和归约算法会自动将计算分配到所有可用的处理器内核中,因此,当稍后在具有更多内核的系统上部署时,今天编写的应用程序将继续扩展。

该模块还提供了QtConcurrent::run()函数,该函数可以在另一个线程中运行任何函数。但是,QtConcurrent::run()仅支持可用于映射,过滤和归约功能的功能子集。 QFuture可用于检索函数的返回值并检查线程是否正在运行。但是,对QtConcurrent::run()的调用仅使用一个线程,不能暂停/继续/取消,也不能查询进度。

有关各个功能的详细信息,请参见Qt Concurrent模块文档。

WorkerScript:QML中的线程

WorkerScript QML类型允许JavaScript代码与GUI线程并行运行。

每个WorkerScript实例可以附加一个.js脚本。调用WorkerScript.sendMessage()时,脚本将在单独的线程(和单独的QML上下文)中运行。脚本运行完毕后,可以将答复发送回GUI线程,该线程将调用WorkerScript.onMessage()信号处理程序。
使用WorkerScript类似于使用已经移到另一个线程的worker QObject。数据通过信号在线程之间传输。

有关如何实现脚本的详细信息以及可以在线程之间传递的数据类型的列表,请参见WorkerScript文档。

选择适当的方法

如上所述,Qt为开发多线程应用程序提供了不同的解决方案。给定应用程序的正确解决方案取决于新线程的目的和线程的生存期。下面是Qt线程技术的比较,然后是一些用例的推荐解决方案。

解决方案对比

方案QThreadQRunnable and QThreadPoolQtConcurrent::run()Qt Concurrent (Map, Filter, Reduce)WorkerScript
编程语言C++C++C++C++QML
可以指定线程优先级   
线程可以运行事件循环    
线程可以通过信号接收数据更新 (received by a worker QObject)    (received by WorkerScript)
可以使用信号控制线程 (received by QThread)   (received by QFutureWatcher) 
可以通过QFuture监视线程 

部分

  
内置的暂停/恢复/取消功能    

使用示例

线程寿命操作方式解决方案
一次性调用在另一个线程中运行新的线性函数,可以选择在运行期间更新进度。Qt提供了不同的解决方案:
1.将函数放在重载的QThread::run()中,然后启动QThread。发出信号以更新进度。
2.将函数放在QRunnable::run()的重载实现中,并将QRunnable添加到QThreadPool。写入线程安全变量以更新进度。
3.使用QtConcurrent::run()运行该函数。写入线程安全变量以更新进度。
一次性调用在另一个线程中运行一个现有函数并获取其返回值。使用QtConcurrent::run()运行该函数。让函数返回时让QFutureWatcher发出finish()信号,然后调用QFutureWatcher :: result()以获取函数的返回值。
一次性调用使用所有可用的内核对容器的所有项目执行操作。 例如,从图像列表生成缩略图。使用Qt Concurrent的QtConcurrent::filter()函数选择容器元素,并使用QtConcurrent::map()函数将操作应用于每个元素。要将输出折叠为单个结果,请改用QtConcurrent::filteredReduced()和QtConcurrent::mappedReduced()。
一次性调用/长期调用在纯QML应用程序中进行长时间的计算,并在准备好结果后更新GUI。将计算代码放在.js脚本中,并将其附加到WorkerScript实例。调用WorkerScript.sendMessage()以在新线程中开始计算。让脚本也调用sendMessage(),以将结果传递回GUI线程。在onMessage中处理结果并在那里更新GUI。
长期调用使对象驻留在另一个线程中,该对象可以根据请求执行不同的任务和/或可以接收要使用的新数据。子类化QObject以创建工作器。实例化此工作对象和一个QThread。将工作程序移动到新线程。通过排队的信号槽连接将命令或数据发送到辅助对象。
长期调用在另一个线程中重复执行耗时的操作。该线程不需要该线程接收任何信号或事件。直接在重新实现QThread :: run()内编写无限循环。启动线程而没有事件循环。让线程发出信号以将数据发送回GUI线程。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值