Qt并发模块Qt Concurrent的使用

Qt并发模块Qt Concurrent的使用

文章目录

1. 简介2. 准备工作2.1 修改`.pro`文件2.2 包含头文件和声明命名空间

3. 运行3.1 运行外部函数3.2 运行成员函数

4. 向函数传递参数的方式5. 获取函数返回值的方式

1. 简介

QtConcurrent 命名空间提供了高级 api,使得无需使用诸如互斥、读写锁、等待条件或信号量等低级线程原语就可以编写多线程程序。使用 QtConcurrent 编写的程序会根据可用的线程处理器核心数量自动调整使用的线程数,这意味着编写的应用程序在部署到多核系统时将自动扩展。 当你发现你自己的程序UI运行不流畅时可以尝试将执行计算的函数放到QtConcurrent::run()中处理,这比改用QThread方便得多。 对于不同的需求可以参照下表:

生命周期开发任务解决方案一次调用在另一个线程中运行一个函数,函数完成时退出线程1.编写函数,使用QtConcurrent::run 运行它2.派生QRunnable,使用QThreadPool::globalInstance()->start()运行它3.派生QThread,重新实现QThread::run() ,使用QThread::start()运行它一次调用需要操作一个容器中所有的项。使用处理器所有可用的核心。一个常见的例子是从图像列表生成缩略图。QtConcurrent 提供了map()函你数来将操作应用到容器中的每一个元素,提供了fitler()函数来选择容器元素,以及指定reduce函数作为选项来组合剩余元素。一次调用一个耗时运行的操作需要放入另一个线程。在处理过程中,状态信息需要发送会GUI线程。使用QThread,重新实现run函数并根据需要发送信号。使用信号槽的queued连接方式将信号连接到GUI线程的槽函数。持久运行生存在另一个线程中的对象,根据要求需要执行不同的任务。这意味着工作线程需要双向的通讯。派生一个QObject对象并实现需要的信号和槽,将对象移动到一个运行有事件循环的线程中并通过queued方式连接的信号槽进行通讯。持久运行生存在另一个线程中的对象,执行诸如轮询端口等重复的任务并与GUI线程通讯。同上,但是在工作线程中使用一个定时器来轮询。尽管如此,处理轮询的最好的解决方案是彻底避免它。有时QSocketNotifer是一个替代。

多线程的一些替代技术(搬运):

替代技术注解QEventLoop::processEvents()在一个耗时的计算操作中反复调用QEventLoop::processEvents() 可以防止界面的假死。尽管如此,这个方案可伸缩性并不太好,因为该函数可能会被调用地过于频繁或者不够频繁。QTimer后台处理操作有时可以方便地使用Timer安排在一个在未来的某一时刻执行的槽中来完成。在没有其他事件需要处理时,时间隔为0的定时器超时事件被相应QSocketNotifierQNetworkAccessManagerQIODevice::readyRead()这是一个替代技术,替代有一个或多个线程在慢速网络执行阻塞读的情况。只要响应部分的计算可以快速执行,这种设计比在线程中实现的同步等待更好。与线程相比这种设计更不容易出错且更节能(energy efficient)。在许多情况下也有性能优势。

2. 准备工作

2.1 修改.pro文件

使用 QtConcurrent 模块,需要在 .pro 中添加: QT += concurrent

2.2 包含头文件和声明命名空间

#include

using namespace QtConcurrent;

3. 运行

3.1 运行外部函数

要在另一个线程中运行一个函数,可以使用 QtConcurrent: : run () :

extern void aFunction();

QFuture future = QtConcurrent::run(aFunction);

这将从默认 QThreadPool 获得的单独线程中运行 aFunction。可以使用 QFuture 和 QFutureWatcher 类来监视函数的状态。如果要使用专用线程池,可以将 QThreadPool 作为第一个参数传递:

extern void aFunction();

QThreadPool pool;

QFuture future = QtConcurrent::run(&pool, aFunction);

3.2 运行成员函数

Run ()也接受指向成员函数的指针。第一个参数必须是常量引用或指向类实例的指针。在调用 const 成员函数时,通常使用 const 引用传递; 通常使用指针传递对于调用修改实例的非 const 成员函数。【强烈建议拓展阅读Qt并发模块Qt Concurrent使用的血泪教训】 例如,在一个单独的线程中调用 QByteArray: : split ()(一个 const 成员函数)是这样做的:

// call 'QList QByteArray::split(char sep) const' in a separate thread

QByteArray bytearray = "hello world";

QFuture > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');

...

QList result = future.result();

调用一个非常量成员函数是这样做的:

// call 'void QImage::invertPixels(InvertMode mode)' in a separate thread

QImage image = ...;

QFuture future = QtConcurrent::run(&image, &QImage::invertPixels, QImage::InvertRgba);

...

future.waitForFinished();

// At this point, the pixels in 'image' have been inverted

4. 向函数传递参数的方式

通过将参数添加到紧跟在函数名之后的 QtConcurrent::run()调用中来完成向函数传递参数。例如:

extern void aFunctionWithArguments(int arg1, double arg2, const QString &string);

int integer = ...;

double floatingPoint = ...;

QString string = ...;

QFuture future = QtConcurrent::run(aFunctionWithArguments, integer, floatingPoint, string);

运行的机制是:在调用 QtConcurrent: : run ()的地方复制每个参数,并在线程开始执行函数时将这些值传递给线程。调用 QtConcurrent: : run ()后对参数所做的更改对线程不可见。

5. 获取函数返回值的方式

返回值可以通过 QFuture 获得:

extern QString functionReturningAString();

QFuture future = QtConcurrent::run(functionReturningAString);

...

QString result = future.result();

注意,QFuture: : result ()函数阻塞并等待结果可用。当函数完成执行并且结果可用时,使用 QFutureWatcher 获得通知。

猜你喜欢

《逆战》免费辅助工具风险与YY频道使用解析
深度剖析Google Play应用商店注册流程:从新手入门到进阶实操
公测当天破解、数据与正版互通,2021年了游戏破解还如此猖狂?
python 中列表 (list) 的超详细说明
best365体育正不正规

python 中列表 (list) 的超详细说明

📅 07-27 ❤️ 391
五分硬币图片及价格,五分硬币价格表,最新五分硬币值多少钱
盘点世界杯期间出现过的十二位女神
365提款问题

盘点世界杯期间出现过的十二位女神

📅 06-28 ❤️ 153
乐旅DVD导航体验全面解析(以乐旅DVD导航怎么样为主题的深度评测)
六问禅道2:需求的状态和研发阶段
GBT36507-2018

六问禅道2:需求的状态和研发阶段

📅 08-11 ❤️ 686
[蓝拳]自定义改版后的蓝拳直伤流派推荐
GBT36507-2018

[蓝拳]自定义改版后的蓝拳直伤流派推荐

📅 07-06 ❤️ 912