Multithreading Technologies in Qt 译文

本文介绍了Qt提供的四种多线程实现方式:QThread、QRunnable与QThreadPool、QtConcurrent模块及WorkerScript QML线程。每种方式都有详细的使用场景说明和技术对比,帮助开发者选择合适的线程技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://doc.qt.io/qt-5/threads-technologies.html

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

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

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


QThread既可以直接实例化,也可以作为一个子类。实例化一个QThread,可以提供一个并行的事件循环,允许在一个次要的线程中调用QObject的槽。子类化一个QThread可以使应用在其本身事件循环开始前去实例一个新线程,或者在没有事件循环情况下,运行并行代码。

 

关于怎样使用QThread 详细示范请查看  QThread class reference 和 threading examples 


QThreadPool 和 QRunnable: 可重用线程

频繁创建销毁线程开销特别大。为减少内存消耗,新的任务可以使用空闲线程。QThreadPool就是可利用线程的集合。

 

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

 

每一个Qt应用都有一个全局的线程池,可以使用QThreadPool::globalInstance()访问。这个全局线程池会根据CPU的内核数量自动维护一个最优的线程数量。然而,可以显示创建和管理一个单独的QThreadPool。


Qt 并发: 高级API

Qt Concurrent 模块提供了一些常见并行计算模版化的高级函数:map,filter,和 reduce。与Qyhread和QRunnable不同,这些高级函数不需要使用类似互斥锁、信号量,这些低级线程原语。相反,这些函数会返回一个QFuture对象,当函数准备好时,会检索函数的结果。QFuture还可以用于查询计算进度,暂停/重置/取消 计算。为了方便,QFutureWadtcher可是通过信号和槽实现QFuture间的交互。

 

Qt Concurrent的map,filter,和 reduce 算法会自动分布到所有可利用的处理器内核上,所以现在编写的应用程序在以后部署更多内核时会继续扩展。

 

这个模块还提供QtConcurrent::run()方法,可以在另一个线程运行任何方法。然而QtConcurrent::run()只提供map,filter,reduce 函数可用属性子集。QFuture可以用于检索函数返回值并检查线程是否仍在运行。但是,调用QtConcurrent::run()只使用一个线程,不能 暂停/重置/取消,和查询进度操作。

 

有关Qt Concurrent模块独特方法的使用细节,请查阅文档Qt Concurrent 。


WorkerScript:  QML 中的线程化

这个WorkerScript QML线程可以使JavaScript代码 和 GUI线程平行化执行。

每一个WorkerScript实例都可以附加一个.js 脚本。当调用 WorkerScript.sendMessage()函数时,脚本可以在一个隔离的线程运行(同时也有一个单独的QML执行期上下文)。当脚本完成运行,会发送一个应答给GUI线程,线程会激活WorkerScript.onMessage()的信号处理器。

 

使用WorkerScript就像是,用一个已经移动到另一个线程去工作的QObject。数据通过信号在线程见传输。

 

查阅WorkerScript文档可以了解实现的细节,和线程间传输的数据类型列表。


选择一个恰当的方式

正如上面所示范的,Qt应用为开发线程应用提供不同的解决方案。基于线程的目的和生命周期才能提供给应用程序一个最佳的方案。下面是各种线程技术对比,后面是一些示例的推荐方案。

方案对比:

特点

QThread

QRunnable和QThreadPool

QtConcurrent::run()

Qt Concurrent(Map, Filter, Reduce)

语言

C++

C++

C++

C++

可以指定线程优先级

  

线程可以执行一个事件循环

 

  

线程可以接收信号更新数据

是(通过一个worker QObject接收)

 

  

线程可以使用信号控制

是(通过QThread接收)

 

 

是(通过QFutureWatcher接收)

线程可以通过QFuture来监视

  

一部分

内置功能可以暂停/重置/取消

 

  

示范用例:

线程生命周期

操作

解决方案

一次性

在另一个线程中执行一个新的线性函数,并可以在运行中更新进度

Qt提供了不同的解决方案:

  • 将函数放在QThread::run()的重新实现中,并启动QThread。发出更新进度的信号。或者
  • 将函数放在QRunnable::run()的重新实现中,并将QRunnable添加到QThreadPool中。写入线程安全变量以更新进度。或者
  • 使用QtConcurrent:: Run()运行函数。写入线程安全变量以更新进度。

一次性

在另一个线程中执行一个已存在的函数,并获取函数返回值

使用QtConcurrent:: Run()运行函数。让QFutureWatcher在函数返回时发出finished()信号,并调用QFutureWatcher::result()来获取函数的返回值。

一次性

使用所有可用的内核,对容器的所有项执行操作。例如,从图像列表中生成缩略图。

使用QtConcurrent的QtConcurrent::filter()函数选择容器里的元素,使用QtConcurrent::map()函数对容器里每个元素执行一个操作。如果需要将所有输出打包成一个结构,那可以使用QtConcurrent::filteredReduced()和QtConcurrent:: mappedrereduced()来代替前面各对应的函数。

一次性/永久性

在一个纯QML应用中执行一个长计算,并且在取到结果后更新GUI

将计算代码放在.js脚本中,并将其附加到WorkerScript实例中。调用WorkerScript.sendMessage()在一个新的线程中启动计算。让 js脚本也调用sendMessage(),将结果传递回GUI线程。在onMessage中处理结果并更新那里的GUI。

永久性

有一个对象存活在另一个线程,可以执行不同的任务请求和/或可以接收新的数据工作。

子类化QObject来创建一个worker。实例化这个worker对象和一个QThread。将worker移动到新线程。通过队列化的信号和槽连接,向worker对象发送命令或数据。

永久性

 在另一个线程中重复执行一个开销大的操作,该线程不需要接收任何信号或事件。

直接在QThread::run()的重新实现中编写无限循环。在没有事件循环的情况下启动线程。让线程发出信号将数据发送回GUI线程。


2021年2月7日星期日 下午6:15:59

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值