【QT进阶】Qt线程与并发之QtConcurrent返回值与run方法的参数说明

本文详细介绍了Qt中的线程与并发编程,重点讲解了QtConcurrent::run函数的参数类型、类成员函数、全局函数以及Lambda表达式的使用,并展示了如何通过QFutureWatcher获取QtConcurrent的返回值。

往期回顾

【QT进阶】Qt线程与并发之线程和并发的简单介绍-优快云博客

【QT进阶】Qt线程与并发之创建线程的三种方法(超详细介绍)-优快云博客

【QT进阶】Qt线程与并发之QtConcurrent的简单介绍-优快云博客

 【QT进阶】Qt线程与并发之QtConcurrent返回值与run方法的参数说明

一、QtConcurrent::run的参数说明

QtConcurrent::run函数的参数类型是模板化的,它可以接受任意可调用对象(函数指针、成员函数指针、函数对象、Lambda表达式等)作为参数。这意味着我们可以传递任何可调用对象作为要在后台线程中执行的任务。

1、QtConcurrent::run函数的签名

template <typename T>
QFuture<T> QtConcurrent::run(T (*functionPointer)(), ...);

这里的T是任务函数的返回类型。我们可以传递一个普通的函数指针,然后在后台线程中执行该函数。除了普通的函数指针外,我们还可以传递其他可调用对象,只要它们符合函数签名的要求即可。

我们看几个参数示例

 2、参数示例

QtConcurrent::run函数的参数,可以是全局函数,也可以是类成员函数,还可以是lambda表达式,同时还可以是不带参数的。

1、类成员函数做run参数

intnum1=0;
int num2 = 0;
QFuture<int> future = QtConcrrent::run(this, &MainWindow::timeTask, num1, num2);

2、用全局函数做参数 

static int timeTask()
int i= 0;
while(i < 123456789)
{
QFuture<int> future = QtConcurrent::run(timeTask);
i++;
}
return i;

3、lambda表达式做参数 

// 在后台线程中执行Lambda表达式
QFuture<int> future = QtConcurrent::run([](){
    // 执行一些操作
    return result;
});

对比一下,首先run方法里是可以带参数的,其次,如果是用类成员函数做参数,是需要添加this并以类名引导,但是如果是用全局函数做参数,则都不用加,直接放函数名就可以了。

二、获取QtConcurrent的返回值 

1、获取方式

获取QtConcurrent的结果,需要使用QFutureWatcher类,链接它的信号finished,然后给watcher设置future,当监控到future执行结束后,可以获取它的执行结果,调用的是result()函数:

qDebug() << "return = " << watcher->result();

2、执行过程

 2.1、创建QFutureWatcher对象

创建一个 QFutureWatcher 对象,用于监视 QFuture 对象的执行状态。

    QFutureWatcher<int>* fw = new QFutureWatcher<int>;
2.2、执行 成员函数 timeTask

在一个新线程中执行 ch74 类的成员函数 timeTask,并传递 num01 和 num02 作为参数。 

    QFuture<int> ft = QtConcurrent::run(this, &ch74::timeTask, num01, num02);
2.3、关联QFuture<int> 对象

将 QFuture<int> 对象与 QFutureWatcher 相关联,以便监视其执行状态。 

fw->setFuture(ft);
2.4、连接 finished信号

连接 QFutureWatcher 的 finished信号,由于给watcher设置future,当监控到future执行结束后,可以获取它的执行结果,调用的是result()函数: 

    connect(fw, &QFutureWatcher<int>::finished, [&]{
        qDebug() << "QFutureWatcher finished";
        qDebug() << "result = " << fw->result();
        //qDebug() << "num1 = " << num1;
        //qDebug() << "num2 = " << num2;
        });
2.5、循环处理事件

通过循环处理事件来保持界面的响应性,直到 QFuture 执行完成。

    //没完成的时候循环处理事件来保持界面的响应性
    while (!ft.isFinished())
    {
        QApplication::processEvents(QEventLoop::AllEvents, 30);
    }

QApplication::processEvents

函数用于处理事件循环中的事件,确保界面的响应性。调用该函数会处理当前事件队列中的所有事件,直到事件队列为空或者达到指定的最大处理时间。 

QEventLoop::AllEvents

参数表示处理所有类型的事件,包括用户输入事件、定时器事件、绘图事件等。

30 参数表示最大处理时间为30毫秒,即函数最多处理30毫秒的事件后返回。这样可以避免在处理事件的过程中阻塞主线程过长时间,确保界面的及时响应。

3、最终代码

#include "ch74.h"
#include <QDebug>
#include <QtConcurrent>
#include <QFuture>
#include <QFutureWatcher>


ch74::ch74(QWidget *parent)
    : QWidget(parent)
{
    ui.setupUi(this);
}

ch74::~ch74()
{}

int ch74::timeTask(int& num1, int& num2)
{
    for (int i = 0; i < 1000; i++)
    {
        num1++;
        num2++;
        qDebug() << num1;
        qDebug() << num2;
    }

    return num1 + num2;
}

void ch74::on_pushButton_clicked()
{
    int num01 = 0;
    int num02 = 0;

    //创建 QFutureWatcher 对象,用于监视一个 QFuture 对象的执行状态
    QFutureWatcher<int>* fw = new QFutureWatcher<int>;

    //通过 QtConcurrent::run 函数在一个新线程中执行 ch74 类的成员函数 timeTask.(并发)
    // num01 和 num02 作为参数传递给 timeTask 函数。
    QFuture<int> ft = QtConcurrent::run(this, &ch74::timeTask, num01, num02);

    //通过 fw->setFuture(ft) 将 QFuture<int> 对象与 QFutureWatcher 相关联,
    //这样 QFutureWatcher 就能监视 QFuture 对象的执行状态
    fw->setFuture(ft);

    //连接 QFutureWatcher 的 finished 信号,当 QFuture 执行完成时输出调试信息,注意这里由于捕获的是指针fw
//所以lambda表达式里用的是[&]而不是[=]
    connect(fw, &QFutureWatcher<int>::finished, [&] {
        qDebug() << "QFutureWatcher finished";
        qDebug() << "result = " << fw->result();
        //qDebug() << "num1 = " << num1;
        //qDebug() << "num2 = " << num2;
        });

    //没完成的时候循环处理事件来保持界面的响应性
    while (!ft.isFinished())
    {
        //QApplication::processEvents 函数用于处理事件循环中的事件,确保界面的响应性
        //调用该函数会处理当前事件队列中的所有事件,直到事件队列为空或者达到指定的最大处理时间
        QApplication::processEvents(QEventLoop::AllEvents, 30);
    }
}

以上就是QtConcurrent返回值与run方法的参数说明的简单介绍

都看到这里了,点个赞再走呗朋友~

加油吧,预祝大家变得更强!

### QtConcurrent::run 的局限性和注意事项 #### 局限性 尽管 `QtConcurrent::run` 提供了一种简单的方式来执行异步任务,但它存在一些固有的局限性: 1. **缺乏对复杂控制流的支持** `QtConcurrent::run` 不支持复杂的并发模式,例如并行映射、过滤或减少操作。它仅适用于简单的函数调用场景[^1]。 2. **无法直接管理线程池资源** 用户无法指定具体的线程池用于运行任务。这意味着对于某些高性能需求的应用程序来说,可能难以优化线程分配策略[^3]。 3. **不支持取消操作** 一旦启动了一个通过 `QtConcurrent::run` 执行的任务,就无法中途停止该任务。这使得在需要动态调整任务状态的情况下显得不够灵活[^1]。 4. **错误处理机制有限** 如果被调用的函数抛出了异常,则这些异常不会被捕获到主线程中。开发者需要自行设计额外的逻辑来捕获和处理潜在的错误情况。 5. **不适合长时间运行的任务** 对于持续时间较长的操作而言,可能会占用过多系统资源而影响其他部分性能表现。因此,在这种情况下应该考虑更高级别的解决方案比如 QThread[^1]。 6. **返回值传递依赖于 QFuture** 虽然可以利用 QFuture 来获取结果,但如果涉及到大量数据传输或者频繁查询进度等情况时,这种方式会增加内存消耗以及编程复杂度[^3]。 7. **缺少显式的线程安全保证** 开发者需确保所使用的共享变量具备必要的同步措施以防竞争条件发生。然而,由于 API 自身并未提供任何内置保护手段,所以很容易忽略这一点从而引发未定义行为[^2]。 8. **跨平台兼容性的隐忧** 尽管官方文档声称此功能具有良好的移植能力,但在实际开发过程中仍可能出现因底层实现差异而导致的行为偏差问题[^1]。 #### 注意事项 为了更好地使用 `QtConcurrent::run` 并规避上述提到的一些缺陷,请注意以下几点建议: - 确保传入给 run 方法的目标对象及其成员方法在整个生命周期内都有效; - 当涉及多个阶段协作完成某项工作流程时,应优先选用更适合表达此类关系的技术栈(如 State Machine Framework)替代单一函数形式; - 在规划项目架构初期就要充分考虑到未来扩展可能性,并据此决定是否采用更加通用但也相对繁琐的手动多线程方案代替当前简化版接口; - 始终牢记即使是最轻量级工具也有可能成为瓶颈所在——定期审查代码质量至关重要! ```cpp // 示例:如何正确设置回调以监控任务结束后的动作 QFuture<void> future = QtConcurrent::run(someHeavyFunction); QFutureWatcher<void> watcher; connect(&watcher, &QFutureWatcher::finished, [&]() { qDebug() << "Task has been completed."; }); watcher.setFuture(future); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值