QtConcurrent和QFuture的使用

部署运行你感兴趣的模型镜像

        在Qt中,有时候我们会遇到这样一种情况,需要执行一个很长时间的操作,这时候我们的主界面就会卡住。我们的通常做法就是把这个很长时间的操作扔到线程里去处理,可以使用标准库中的线程也可以使用QThread。

        如果我们要在这个很长时间的操作之后,在UI上显示一些东西,或者改变一些UI上的控件的状态。这种时候标准库的线程就不是很好用了,通常这种时候我们会使用QThread,创建一个新的类继承QObject,然后再这个新的类里面写一堆信号和槽,和主线程通讯传递消息改变UI界面。但是这种太麻烦了,每次都要新创建一个类和一堆信号,十分不好管理。

        在查了资料后,我发现了Qt有提供专门并发的类QtConcurrent,以及接收异步计算结果的QFuture类。在这里记录一下QtConcurrent和QFuture的使用。


介绍:

        Concurrent是并发的意思,而QtConcurrent同std一样,是一个命名空间(namespace),想使用它需要先在Project工程文件中导入模块,并包含头文件QtConcurrent/QtConcurrent。

QT += concurrent

#include <QtConcurrent/QtConcurrent>

        QtConcurrent提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。

        QtConcurrent中使用最多的是它的run()函数,每调用一次QtConcurrent::run()函数,就会新建立一个线程运行我们让它执行的函数。run()函数的返回值QFuture类型的,run()函数是有很多重载,这里就简单讲几个常用的。


QtConcurrent::run示例:

调用全局函数: 

函数原型:
QFuture<T> QtConcurrent::run(Function func, ...)

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QDebug>
#include <QThread>
#include <QFuture>
#include <QtConcurrent/QtConcurrent>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

QString func(QString content)
{
    QString str = QString("%1 %2 %3.").arg(__FUNCTION__).arg(content).arg(quintptr(QThread::currentThreadId()));
    return str;
}

void MainWindow::on_pushButton_clicked()
{
    QFuture<QString> fut1 = QtConcurrent::run(func, QString("Thread_1"));// 用QFuture获取该函数的运行结果,参数2:向func函数传递的参数
    QFuture<QString> fut2 = QtConcurrent::run(func, QString("Thread_2"));
    QString result1 = fut1.result();
    QString result2 = fut2.result();
    qDebug() << result1;
    qDebug() << result2;
    fut1.waitForFinished();// waitForFinished()保证线程执行完毕
    fut2.waitForFinished();
}

        这里使用QFuture的result()函数获取QtConcurrent::run()执行的函数的返回值,然后打印出来,打印结果如下:

调用匿名函数: 

         上面的示例中的func也可以改成匿名函数,只是写法上不同,结果都是一样的:

QFuture <QString> future =  QtConcurrent::run([=](){
    QString str = QString("%1 %2 %3.").arg(__FUNCTION__).arg("Thread_1").arg(quintptr(QThread::currentThreadId()));
    return str;
});
QFuture <QString> future2 = QtConcurrent::run([=](){
    QString str = QString("%1 %2 %3.").arg(__FUNCTION__).arg("Thread_2").arg(quintptr(QThread::currentThreadId()));
    return str;
});

 调用成员函数:

         同样的,QtConcurrent::run()也可以调用成员函数,第一个参数必须是一个const引用或一个指向该类实例的指针,第二个参数是函数指针:

QString MainWindow::func(QString content)
{
    QString str = QString("%1 %2 %3.").arg(__FUNCTION__).arg(content).arg(quintptr(QThread::currentThreadId()));
    return str;
}

QFuture<QString> fut1 = QtConcurrent::run(this, &MainWindow::func, QString("Thread_1"));
QFuture<QString> fut2 = QtConcurrent::run(this, &MainWindow::func, QString("Thread_2"));

调用其他类的成员函数: 

         调用其他类的成员函数,包括Qt提供的函数也是同样的方法:

QByteArray bytearray = "hello,world";
QFuture<QList<QByteArray>> future = QtConcurrent::run(bytearray, &QByteArray::split, ',');

 使用线程池中的线程调用函数:

// 函数原型

template <typename T> QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)


使用QFutureWatcher监视线程:

        QFuture 表示异步计算的结果,QFutureWatcher 则允许使用信号和槽监视 QFuture,也就是说,QFutureWatcher 是为 QFuture 而生的。

        示例,开始计算并当完成时通过槽获取结果:

// 实例化对象,并连接槽到 finished() 信号,等线程中函数完成后就会触发连接的槽。
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, &QFutureWatcher<QVector<complex<double>>>::finished, &myObject, &MyClass::handleFinished);
 
// 开始计算
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);

        匿名函数的写法:

QFutureWatcher<QVector<complex<double>>>* pwatcher = new QFutureWatcher<QVector<complex<double>>>;
QFuture<QVector<complex<double>>> future = QtConcurrent::run([=]() {
    QVector<complex<double>> result;
    // 费时操作在这里执行,之后返回QVector<complex<double>>类型的结果

    return result;
});
connect(pwatcher, &QFutureWatcher<QVector<complex<double>>>::finished, this, [=]() {
    // 使用pwatcher->result()获取在QtConcurrent::run()中的返回值
    QVector<complex<double>> img2result = pwatcher->result();
    // 执行费时操作完成之后的操作,例如改变UI界面的控件
});
pwatcher->setFuture(future);

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

### 使用 QtConcurrent 进行并发编程优化折线图大数据处理 在使用 Qt 绘制大规模折线图时,数据预处理(如降采样、分段加载)通常会成为性能瓶颈。为提升处理效率,可以利用 Qt 提供的 `QtConcurrent` 模块进行多线程并发处理,从而将计算任务分配到多个 CPU 核心上执行,显著减少主线程的阻塞时间。 #### 并发处理大规模折线图数据 在折线图数据量大的情况下,可以将数据预处理操作(如降采样、分段、滤波等)封装为可并发执行的函数,并通过 `QtConcurrent::run()` 或 `QtConcurrent::mapped()` 等接口提交给线程池执行。例如,可以将原始数据切分为多个子集,并行处理后再合并结果用于图表绘制。 ```cpp #include <QtConcurrent/QtConcurrentRun> #include <QVector> #include <QPointF> // 模拟一个数据降采样函数 QVector<QPointF> downsampleData(const QVector<QPointF> &rawData, int step) { QVector<QPointF> result; for (int i = 0; i < rawData.size(); i += step) { result.append(rawData[i]); } return result; } // 主线程中调用并发处理 QFuture<QVector<QPointF>> future = QtConcurrent::run(downsampleData, rawData, 10); ``` 上述代码中,`downsampleData` 函数负责对原始数据进行降采样处理,`QtConcurrent::run` 将其放入后台线程执行,避免阻塞 UI 线程。当处理完成后,可以通过 `QFutureWatcher` 或信号槽机制将结果传递回主线程用于 QChart 渲染 [^1]。 #### 避免线程冲突与资源竞争 在使用 `QtConcurrent` 时,应避免多个线程同时访问或修改共享资源(如全局变量、UI 控件)。可以通过将数据封装为只读结构或使用互斥锁(`QMutex`)进行保护。此外,所有与 QChart 的交互(如调用 `append()` 方法)必须在主线程中完成,以确保 Qt 的线程安全机制正常工作 [^2]。 #### 与 QChart 的结合使用 在 QChart 中绘制大规模数据时,可以将原始数据分批次处理并异步加载。例如,在用户缩放或平移图表时,根据当前视图范围动态加载对应的数据子集,并通过 `QtConcurrent` 预处理该子集,从而实现高效的交互式图表渲染。 ```cpp void ChartView::updateVisibleData(const QRectF &visibleRange) { // 提取当前视图范围内的数据 QVector<QPointF> visibleRawData = extractDataInRange(rawData, visibleRange); // 异步降采样 QFuture<QVector<QPointF>> future = QtConcurrent::run(downsampleData, visibleRawData, 5); // 使用 QFutureWatcher 监听结果 connect(&watcher, &QFutureWatcher<QVector<QPointF>>::finished, this, [this]() { series->replace(watcher.future().result()); }); watcher.setFuture(future); } ``` 上述方法可以在用户操作时动态调整显示的数据量,避免一次性加载所有数据,从而显著提升性能 [^1]。 --- ### 示例代码:QtConcurrent 异步处理折线图数据 ```cpp #include <QtConcurrent/QtConcurrentRun> #include <QChartView> #include <QLineSeries> #include <QVector> #include <QPointF> #include <QFutureWatcher> QT_CHARTS_USE_NAMESPACE class ChartWorker : public QObject { Q_OBJECT public: explicit ChartWorker(QObject *parent = nullptr) : QObject(parent) {} void process(const QVector<QPointF> &data) { future = QtConcurrent::run(this, &ChartWorker::downsample, data); watcher.setFuture(future); } signals: void dataProcessed(const QVector<QPointF> &result); private: QFuture<QVector<QPointF>> future; QFutureWatcher<QVector<QPointF>> watcher; QVector<QPointF> downsample(const QVector<QPointF> &data) { QVector<QPointF> result; for (int i = 0; i < data.size(); i += 10) { result.append(data[i]); } return result; } }; // 主程序中连接信号与槽 ChartWorker *worker = new ChartWorker(); QLineSeries *series = new QLineSeries(); connect(worker, &ChartWorker::dataProcessed, [=](const QVector<QPointF> &result) { series->replace(result); }); worker->process(rawData); ``` --- ### 总结 在使用 QChart 绘制大规模折线图时,利用 `QtConcurrent` 实现并发数据处理是一种高效的方式。通过将数据降采样、分段加载等操作放到后台线程执行,可以显著提升应用响应速度用户体验。同时,应合理设计线程间的通信机制,确保所有与 UI 相关的操作都在主线程中完成 。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值