自学内容网 自学内容网

QtConcorrent学习、以及与QThread之间的联系

目录

一、QtConcorrent 概述

1、功能

2、特点

3、使用场景

4、QtConcurrent::run

5、应用示例

5、挑战和解决方案

6、QtConcurrent的重要性和价值

二、QFuture

1、主要特点和功能

2、应用示例

三、QtConcorrent与QThread

1、抽象级别和易用性

2. 线程管理和资源利用

3. 线程间通信和同步

4、适用场景

5、应用示例


一、QtConcorrent 概述

1、功能

        QtConcurrent是Qt框架中的一个高级多线程编程模块,它提供了一组高层次的API,使得开发者能够编写多线程程序而无需直接使用线程、互斥锁或信号量等低级同步原语。QtConcurrent通过线程池、Futures和Promises等机制,自动管理线程的生命周期和同步问题,从而简化了并发编程的复杂性。

2、特点

(1)简单易用:QtConcurrent提供了一组简单易用的函数,允许开发者专注于业务逻辑的实现,而无需关注线程的创建、管理和同步等细节。

(2)高效可靠:QtConcurrent采用了现代化的并行编程技术,包括线程池等,可以充分利用多核CPU资源,提高程序的运行效率和性能。

(3)跨平台支持:QtConcurrent框架可以在多种操作系统平台上运行,包括Windows、Linux和macOS等。

(4)集成Qt信号和槽机制:QtConcurrent与Qt的信号和槽机制无缝集成,便于开发者在并发任务中处理信号和槽事件。

3、使用场景

QtConcurrent适用于需要并行处理大量数据或执行复杂计算任务的场景,如图像处理、数据分析、科学计算等。通过QtConcurrent,开发者可以轻松地实现任务的并行化,提高程序的执行效率和响应性。

4、QtConcurrent::run

QFuture<T> QtConcurrent::run(Function function, ...)

QtConcurrent::run(QThreadPool::globalInstance(), function, ...);

(1)第一种调用方式:

//函数声明
static void* FuncThread(void* arg);

//函数定义
void *ClassXX::FuncThread(void *arg)
{
    ClassXX *p = (ClassXX *)arg;
    // todo

    return nullptr;
}


//调用
QtConcurrent::run([](){
     FuncThread(ClassXX::instance());
});

(2)第二种调用方式

// 一个耗时的任务函数  
int expensiveFunction(int number) {  
    // 模拟耗时操作  
    QThread::sleep(1);  
    return number * number;  
}  

// 启动异步任务  
QFuture<int> future = QtConcurrent::run(expensiveFunction, 42);  
5、应用示例

假设有一个图像处理应用,需要对大量图片进行缩放处理。使用QtConcurrent,可以轻松地实现图片的并行处理。示例代码如下:

#include <QtConcurrent>  
#include <QImage>  
#include <QDebug>  
  
QImage resizeImage(const QImage &image, int newSize) {  
    QImage scaledImage = image.scaled(newSize, newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);  
    return scaledImage;  
}  
  
// 假设有一个包含图片路径的QStringList对象images  
QStringList images;  
// ... 填充images列表  
  
// 使用QtConcurrent::mapped对图片进行并行缩放处理  
QFuture<QImage> future = QtConcurrent::mapped(images, [newSize](const QString &path) {  
    QImage image(path);  
    return resizeImage(image, newSize);  
});  
  
// 等待所有任务完成  
future.waitForFinished();  
  
// 处理结果  
foreach (const QImage &image, future.results()) {  
    // ... 处理缩放后的图片  
    qDebug() << "Processed image";  
}
5、挑战和解决方案

(1)线程安全问题:在多线程环境下,需要避免不同线程之间的数据竞争。解决方案是尽量避免共享数据,如果确实需要共享数据,可以使用Qt提供的同步机制(如QMutex、QReadWriteLock等)来进行线程同步和资源共享。

(2)任务调度和负载均衡:QtConcurrent的线程池会根据系统的硬件和可用资源动态地管理线程数量。然而,在某些情况下,开发者可能需要手动调整线程池的大小以优化性能。解决方案是创建自定义的QThreadPool对象,并设置适当的线程数量。

(3)错误处理:QtConcurrent不直接提供错误处理信号,但可以通过QFuture和异常捕获机制来处理任务中的错误。解决方案是在任务函数中捕获并处理异常,或者通过QFuture的接口来检查任务是否成功完成。

6、QtConcurrent的重要性和价值

QtConcurrent的重要性和价值在于它简化了并发编程的复杂性,使得开发者能够更加专注于业务逻辑的实现。

通过QtConcurrent,开发者可以轻松地实现任务的并行化,提高程序的执行效率和响应性。在现代应用程序中,并发编程变得越来越重要,QtConcurrent为开发者提供了一种简单、高效、可靠的多线程编程方式,有助于提升应用程序的性能和用户体验。因此,鼓励学习者积极学习和使用QtConcurrent,以更好地应对现代应用程序中的并发编程挑战。

二、QFuture

QFuture 是 QtConcurrent 框架中的一个核心类,它提供了一种访问异步计算结果的方式。当你使用 QtConcurrent 的函数(如 QtConcurrent::runQtConcurrent::mappedQtConcurrent::filtered 等)启动一个异步任务时,这些函数会返回一个 QFuture 对象。通过这个对象,你可以查询异步任务的执行状态,获取任务的结果,甚至可以取消正在执行的任务。

1、主要特点和功能

(1)查询状态QFuture 提供了多种方法来查询异步任务的执行状态,包括是否已开始、是否正在运行、是否已完成或是否被取消。

(2)获取结果:当异步任务完成时,你可以通过 QFuture 对象来获取任务的结果。如果任务还没有完成,你也可以选择阻塞当前线程直到任务完成并获取结果。

(3)结果迭代器:对于返回容器类型结果的任务,QFuture 提供了迭代器接口,允许你遍历结果集中的每个元素。

(4)取消任务:某些类型的异步任务支持取消操作。你可以通过 QFuture 对象来请求取消正在执行的任务。但请注意,并非所有类型的任务都可以被取消,这取决于任务的性质以及它是否响应取消请求。

(5)与Qt事件循环集成QFuture 与 Qt 的事件循环紧密集成,允许你在任务完成时通过信号和槽机制接收通知。

2、应用示例

以下是一个使用 QFuture 的简单示例,它演示了如何启动一个异步任务并查询任务的状态和结果:

#include <QCoreApplication>  
#include <QtConcurrent>  
#include <QDebug>  
  
// 一个耗时的任务函数  
int expensiveFunction(int number) {  
    // 模拟耗时操作  
    QThread::sleep(1);  
    return number * number;  
}  
  
int main(int argc, char *argv[]) {  
    QCoreApplication a(argc, argv);  
  
    // 启动异步任务  
    QFuture<int> future = QtConcurrent::run(expensiveFunction, 42);  
  
    // 可以在这里执行其他任务...  
  
    // 等待异步任务完成  
    while (!future.isFinished()) {  
        qDebug() << "Waiting for the result...";  
        QCoreApplication::processEvents(); // 处理事件循环,以便UI保持响应  
    }  
  
    // 获取并打印结果  
    if (future.isResultReadyAt(0)) { // 通常不需要检查索引,除非处理的是mapped或filtered等函数的结果  
        qDebug() << "Result:" << future.result();  
    }  
  
    return a.exec();  
}

请注意,在实际应用中,通常不会使用 while 循环和 QCoreApplication::processEvents() 来等待异步任务完成。相反,你会更倾向于使用信号和槽机制,在任务完成时接收通知。

注意事项

(1)并非所有类型的任务都可以被取消。任务是否支持取消取决于任务的实现以及它是否检查并响应取消请求。

(2)当你使用 QFuture 的 result() 方法时,如果任务尚未完成,该方法会阻塞调用线程直到任务完成。因此,在GUI应用程序中,你应该避免在主线程(即UI线程)中调用 result(),以免阻塞UI。相反,你应该使用信号和槽来在任务完成时接收通知。

(3)QFuture 对象本身并不存储结果数据。它只是提供了访问结果的接口。因此,如果你需要保存结果以供将来使用,你应该在获取结果后立即将其存储在适当的容器中。

三、QtConcorrent与QThread

QtConcurrent 和 QThread 是 Qt 框架中用于多线程编程的两种不同方式,它们各有特点和适用场景。以下是它们之间的主要区别:

1、抽象级别和易用性
  • QtConcurrent
    • 提供了高级别的抽象,使得开发者可以更容易地编写并发代码,而无需直接处理线程的创建、管理和销毁等底层细节。
    • 非常适合于执行短期任务或不需要复杂线程交互的场景。
    • 使用静态函数,如 QtConcurrent::run(),来启动并发任务,并返回 QFuture 对象,用于查询任务状态和结果。
  • QThread
    • 提供了更低级别的线程控制,允许开发者更细致地管理线程的生命周期和内部逻辑。
    • 需要通过继承 QThread 类并重写 run() 方法来定义线程执行的任务。
    • 适用于需要长时间运行或需要复杂线程交互的场景,如需要在线程中维护事件循环、处理信号槽等。
2. 线程管理和资源利用
  • QtConcurrent
    • 自动管理线程池,根据系统资源和任务需求动态调整线程数量。
    • 提高了资源利用率,减少了线程创建和销毁的开销。
    • 不支持直接控制线程的优先级或堆栈大小等属性。
  • QThread
    • 需要开发者手动管理线程的生命周期,包括创建、启动、停止和销毁。
    • 允许设置线程的优先级、堆栈大小等属性,以适应不同的应用场景。
    • 如果没有正确地管理线程,可能会导致资源泄漏或死锁等问题。
3. 线程间通信和同步
  • QtConcurrent
    • 提供了 QFuture 和 QFutureWatcher 类来查询任务状态和结果,但不直接支持信号槽机制进行线程间通信。
    • 如果需要在线程间传递复杂数据或进行复杂的交互,可能需要结合其他机制(如 QMutexQSemaphore 等)来实现。
  • QThread
    • 完全支持 Qt 的信号槽机制进行线程间通信,使得线程间的交互更加直观和方便。
    • 可以通过信号槽来安全地传递消息和数据,避免直接访问共享资源导致的竞争条件和死锁问题。
4、适用场景
  • QtConcurrent
    • 适用于执行短期任务或不需要复杂线程交互的场景。
    • 例如,并行处理数据集合、执行耗时计算等。
  • QThread
    • 适用于需要长时间运行或需要复杂线程交互的场景。
    • 例如,在后台线程中处理网络请求、数据库操作、文件读写等,同时需要实时更新 GUI 或与其他线程进行交互。

综上所述,QtConcurrent 和 QThread 各有优缺点,开发者应根据具体的应用场景和需求来选择合适的工具。如果需要快速编写并发代码并简化线程管理,可以选择 QtConcurrent;如果需要更细致地控制线程的生命周期和内部逻辑,或需要进行复杂的线程间交互,可以选择 QThread

5、应用示例

(1)使用QThread

QThread是Qt中用于管理线程的类。要使用QThread,通常需要创建一个继承自QThread的类,并重写其run()方法。在这个run()方法中,你可以放置需要在新线程中执行的代码。

基本步骤

  1. 创建QThread的子类:在这个子类中,重写run()方法以包含线程执行的代码。

  2. 实例化子类:创建一个或多个你的QThread子类的实例。

  3. 启动线程:调用线程的start()方法来启动线程。这将调用你重写的run()方法。

  4. 线程同步和通信:使用Qt的信号和槽机制、互斥锁(QMutex)、条件变量(QWaitCondition)等进行线程间的同步和通信。

  5. 结束线程QThreadrun()方法执行完毕后,线程将自动结束。你也可以在适当的时候调用quit()exit()方法来请求线程退出。

class WorkerThread : public QThread  
{  
    Q_OBJECT  
  
protected:  
    void run() override {  
        // 在这里执行需要在新线程中运行的代码  
        qDebug() << "Thread is running";  
  
        // 模拟耗时操作  
        QThread::sleep(5);  
  
        emit resultReady("Done");  
    }  
  
signals:  
    void resultReady(const QString &result);  
};  
  
// 使用  
WorkerThread *thread = new WorkerThread();  
QObject::connect(thread, &WorkerThread::resultReady, this, [](const QString &result){  
    qDebug() << "Result:" << result;  
});  
thread->start();

(2)使用QtConcurrent模块

QtConcurrent提供了高级别的API来简化多线程编程。它使用Qt的元对象系统来跨越线程边界传递参数和返回值,而无需编写额外的线程同步代码。

基本用法

  1. 使用QtConcurrent::run():这个函数接受一个函数或可调用对象以及它的参数,并在一个线程池中异步执行它。它返回一个QFuture对象,可以用来查询结果或取消操作。

  2. 连接QFuture的信号QFuture提供了信号,如finished()resultReadyAt(),可以用来在异步操作完成时接收通知。

#include <QtConcurrent>  
  
void myFunction(int arg) {  
    // 执行一些操作  
    qDebug() << "Function is running with argument:" << arg;  
}  
  
// 使用  
QFuture<void> future = QtConcurrent::run(myFunction, 42);  
QObject::connect(&future, &QFuture::finished, [](){  
    qDebug() << "Function finished";  
});

在这个例子中,myFunction将在后台线程中异步执行,而主线程则继续执行其他任务。当myFunction完成时,将发出finished()信号,并可以在相应的槽函数中处理完成后的逻辑。

总结

根据你的需求,你可以选择使用QThreadQtConcurrent来实现多线程编程。如果你需要更细粒度的控制(如设置线程的优先级或堆栈大小),或者你的线程需要长时间运行且需要与Qt的其他部分进行复杂的交互,那么QThread可能更适合你。相反,如果你只是需要简单地并行执行一些任务,并且不需要直接控制线程的生命周期,那么QtConcurrent可能是一个更简单、更方便的选择。


原文地址:https://blog.csdn.net/bigger_belief/article/details/142333299

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!