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

ops/2024/9/20 3:40:41/ 标签: 学习

目录

一、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;// todoreturn 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可能是一个更简单、更方便的选择。


http://www.ppmy.cn/ops/113233.html

相关文章

C++速通LeetCode简单第6题-环形链表

快慢指针真的很好用&#xff01; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:bool hasCycle(ListNode *head) {//快慢指针ListNode* fast…

2024.9.19

[ABC266F] Well-defined Path Queries on a Namori 题面翻译 题目描述 给定一张有 N N N 个点、 N N N 条边的简单连通无向图和 Q Q Q 次询问&#xff0c;对于每次询问&#xff0c;给定 x i , y i x_i,y_i xi​,yi​&#xff0c;表示两点的编号&#xff0c;请你回答第 x i …

微信小程序开发第三课

1 wxml语法 1.1 模版语法 # 1 在页面 xx.js 的 Page() 方法的 data 对象中进行声明定义 # 2 在xx.wxml 中使用 {{}} 包裹&#xff0c;显示数据 # 3 可以显示如下&#xff0c;不能编写js语句或js方法-变量-算数运算-三元运算-逻辑判断# 4 只是单纯通过赋值&#xff0c;js中…

跨界融合,GIS如何赋能游戏商业——以《黑神话:悟空》为例

在数字化时代&#xff0c;地理信息系统&#xff08;GIS&#xff09;技术正以其独特的空间分析和可视化能力&#xff0c;为游戏产业带来革命性的变革。《黑神话&#xff1a;悟空》作为中国首款3A级别的动作角色扮演游戏&#xff0c;不仅在游戏设计和技术上取得了突破&#xff0c…

【华为杯】第二十一届中国研究生数学建模竞赛

“华为杯”第二十一届中国研究生数学建模竞赛即将开始&#xff0c;梦想科研社给大家整理一些比赛信息&#xff0c;在正式开赛后&#xff0c;我们也会持续分享一些课题的分析以及代码&#xff0c;有需要的可以联系我们获取资料信息哦 一、时间节点 1.加密赛题开始下载时间&…

大数据新视界 --大数据大厂之SaaS模式下的大数据应用:创新与变革

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

计算机三级网络技术总结(二)

RPR使用统计复用的方法传输IP分组IEEE802.16a用于固定结点接入ADSL技术为速率非对称型&#xff0c;上行速率为64kbps~640kbpsRAID是磁盘阵列技术在一定程度上可以提高磁盘存储容量但不能提高容错能力中继器工作在物理层VTP有三种工作模式:VTP Server、VTP Client 和VTP Transpa…

Spring4-IoC2-基于注解管理bean

目录 开启组件扫描 使用注解定义bean Autowired注入 场景一&#xff1a;属性注入 场景二&#xff1a;set注入 场景三&#xff1a;构造方法注入 场景四&#xff1a;形参注入 场景五&#xff1a;只有一个构造函数&#xff0c;无注解 场景六&#xff1a;Autowired和Quali…

通信工程学习:什么是HSS归属用户服务器

HSS&#xff1a;归属用户服务器 HSS&#xff08;归属用户服务器&#xff0c;Home Subscriber Server&#xff09;是IP多媒体子系统&#xff08;IMS&#xff09;中控制层的一个重要组成部分&#xff0c;它扮演着存储和管理用户相关信息的核心角色。以下是关于HSS归属用户服务器的…

计算机毕业设计选题推荐-校园车辆管理系统-Java/Python项目实战(亮点:数据可视化分析、账号锁定)

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

while循环及简单案例

//循环是流程控制中的一个重要分支 //流程控制 条件判断 循环 逻辑处理 //循环的目的和意义 //循环的目的是为了执行一块代码 //循环的意义是为了简化代码。增加代码的复用性 /* //例如输出0-100的数…

Pytorch是如何做显存管理的

参考资料&#xff1a; GPT的回答 自己的实验结果 之前自己在用Pytorch跑模型训练的时候产生了如下一系列问题&#xff1a;1)Pytorch使用的cuda显存什么时候释放 2)什么时候会导致显存堆积 3)如何监控显存的使用。经过查找资料后找到了这些问题的答案&#xff0c;现在记录在此&a…

Node.js快速入门

【图书介绍】《Node.jsMongoDBVue.js全栈开发实战》-CSDN博客 《Node.jsMongoDBVue.js全栈开发实战&#xff08;Web前端技术丛书&#xff09;》(邹琼俊)【摘要 书评 试读】- 京东图书 (jd.com) Node.js运行环境搭建-CSDN博客 本节将介绍如何快速入门Node.js。 1.3.1 Node.…

C++——给出一个不多于5位的正整数,要求:(1)求出它是几位数 (2)分别打印出每一位数字 (3)按逆序打印出各位数字,例如原数为321,应输出123。

没注释的源代码 #include <iostream> using namespace std; int main() { int x,a,b,c,d,e,f,len; cout<<"请输入一个不多于5位的正整数:"; cin>>x; if (x>999999) cout<<"输入错误&#xff01;"<<en…

linux服务器配置及服务器资源命令使用查看

在做性能压测之前&#xff0c;需要对被测服务器有所了解&#xff0c;常用的服务器资源有&#xff1a;CPU、内存、硬盘、网络、进程等 sysstat百度网盘&#xff1a;sysstat 提取码: 0000 一、查看系统服务器配置信息 常用命令&#xff1a;cat /proc/cpuinfo或者lscpu、pidsta…

根据NVeloDocx Word模板引擎生成Word(五)

前面几篇基本上介绍完了NVeloDocx的基础用法&#xff0c;绝大部分的需求其实都是这些基础的东西&#xff0c;本篇将介绍2个不常用但是实际的业务场景&#xff1a; 1、图片列表输出&#xff1b; 比如在E6开发平台生成的客户端中&#xff0c;图片列表往往是这样显示的&#xff…

循环神经网络

文章目录 序列模型文本预处理语言模型和数据集循环神经网络从0实现简洁实现 GRU从0开始实现简洁实现一个例子 LSTM从0实现简洁实现 深度循环神经网络双向循环神经网络机器翻译与数据集编码器-解码器序列到序列的学习 !nvidia-smiWed Sep 11 07:23:53 2024 -------------…

Python实现一个简单的爬虫程序(爬取图片)

目录 1、安装爬虫Scrapy 2、新建爬虫项目 3、配置爬虫 4、编写爬虫代码,爬取百度图片 5、运行爬虫程序 使用爬虫需要遵守相关法律和规范! 1、安装爬虫Scrapy 编程环境是Anaconda,其安装和使用见我之前的文章,这里就不赘述了。 首先安装爬虫Scrapy,为了加快下载速度…

Nacos和Eureka的区别

前言 Nacos 和 Eureka 都是用于服务发现与注册的工具&#xff0c;它们在微服务架构中都扮演着重要角色。 Nacos与Eureka的共同点 都支持服务注册和服务拉取都支持服务提供者心跳方式做健康检测 Nacos与Eureka的区别 Nacos支持服务端主动检测提供者状态&#xff1a;临时实例采…

Unreal像素流ubantu os部署细节

Unreal像素流ubantu os部署细节 前言Docker 运行时环境部署&#xff1a;1、Docker镜像资源获取&#xff1a;2、镜像资源构造容器&#xff1a;3、运行容器以及控制台用户切换&#xff1a;4、映射路径内容启动程序 注意事项 前言 在一般的Linux服务进行Unreal服务的部署&#xf…