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;// 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.rhkb.cn/news/427245.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apache subversion 编译流程

目录 1. 概述2. 依赖库简介2.1 Expat2.2 Apache apr2.3 Apache apr-iconv2.4 Apache apr-util2.5 Zlib2.6 OpenSSL2.7 Sqlite2.8 Apache Serf2.9 Apache subversion3. 编译3.1 Expat编译3.1.1 源码信息3.1.2 CMake-GUI3.1.3 编译步骤3.2 APR编译3.2.1 源码信息3.2.2 编译步骤3.…

【笔记】2.1 半导体三极管(BJT,Bipolar Junction Transistor)

一、结构和符号 1. 三极管结构 常用的三极管的结构有硅平面管和锗合金管两种类型。各有PNP型和NPN型两种结构。 左图是NPN型硅平面三极管,右图是PNP型锗合金三极管。 从图中可见平面型三极管是先在一块大的金属板上注入杂质使之变成N型,然后再在中间注入杂质使之变成P型,…

倒序循环(一)

题目描述 输入一个正整数n&#xff0c;输出从 n~ 1 递减的序列。 输入格式 一行一个整数 n 输出格式 n 行&#xff0c;每行一个符合题目要求的整数 样例数据 样例输入#1 5样例输出#1 5 4 3 2 1样例输入#2 6样例输出#2 6 5 4 3 2 1数据范围 对于100%的数据&#xff…

Java企业面试题2

1.语言的分代&#xff1a; 第1代&#xff1a;机器语言 机器语言是最底层的计算机编程语言&#xff0c;它是由二进制数构成的一系列指令&#xff0c;直接与计算机硬件交互。每个二进制位模式代表一条特定的指令或数据地址。因为它是直接在硬件上执行的&#xff0c;所以运行效率…

Java 23 的12 个新特性!!

Java 23 来啦&#xff01;和 Java 22 一样&#xff0c;这也是一个非 LTS&#xff08;长期支持&#xff09;版本&#xff0c;Oracle 仅提供六个月的支持。下一个长期支持版是 Java 25&#xff0c;预计明年 9 月份发布。 Java 23 一共有 12 个新特性&#xff01; 有同学表示&…

xmake与包管理:又一个现代c++构建工具?

个人博客:Sekyoro的博客小屋 个人网站:Proanimer的个人网站 主要起因是我在逛Reddit帖子时,看到关于一些c构建系统的评价. cmake似乎有些过于复杂,它与vcpkg,conan的包管理之间的"融合"可能在有些时候也显得麻烦. 一些人尝试了我没见过的选项, 所以这里主要试试除了…

Java重修笔记 第五十七天 坦克大战(七)多线程基础 - 编程练习

1. 线程之间的协调控制&#xff08;通知方式&#xff09; public class Homework04 {public static void main(String[] args) {// 在 main 方法中启动两个线程// 第一个线程内循环打印 1 到 100 以内的整数// 直到第二个线程从键盘读取到 "Q" 指令后结束第一个线程…

深入剖析大模型原理——Qwen Blog

1. 输入部分 Text&#xff1a;原始输入文本&#xff0c;模型需要处理的自然语言数据。Tokenizer&#xff1a;分词器&#xff0c;将输入文本转换为词汇表中的索引&#xff08;ID&#xff09;&#xff0c;便于后续处理。Input_ids&#xff1a;经过分词处理后的ID序列&#xff0c…

交流回馈老化测试负载的智能升级

在电子设备的生产过程中&#xff0c;老化测试是不可或缺的环节。老化测试主要是通过模拟设备长时间工作的情况&#xff0c;检测设备在经过一定时间的使用后&#xff0c;其性能是否会发生降低&#xff0c;是否存在安全隐患等。老化测试负载是老化测试中的重要组成部分&#xff0…

今日所学啊

ArcGIS打不开焦点统计如何解决_arcgis焦点统计打不开-CSDN博客 好吧其实最后焦点统计还是不行&#xff0c;我就去ArcGIS Pro里做焦点统计了哈哈哈哈哈哈哈 visual studio多工程项目管理_visual studio 的模块管理-CSDN博客 1.今天成功#include <QNetworkReply>不画红线…

【MYSQL表的增删改查(基础)】

MYSQL表的增删改查&#xff08;基础&#xff09; 一、CRUD二、新增&#xff08;Create&#xff09;2.1 单行数据全列插入2.2 多行数据指定列插入 三、查询&#xff08;Retrieve&#xff09;3.1 全列查询3.2 指定列查询3.3 查询字段为表达式3.3.1 表达式不包含字段3.3.2 表达式包…

react是什么?

文章目录 核心特点1. **组件化**2. **虚拟 DOM** 3. **声明式编程**4. **单向数据流**5. **JSX 语法**6. **Hooks** react的优势劣势 React 是一个由 Facebook 开发和维护的开源 JavaScript 库&#xff0c;用于构建用户界面&#xff0c;特别是单页应用程序&#xff08;SPA&…

[PICO VR眼镜]眼动追踪串流Unity开发与使用方法,眼动追踪打包报错问题解决(Eye Tracking/手势跟踪)

前言 最近在做一个工作需要用到PICO4 Enterprise VR头盔里的眼动追踪功能&#xff0c;但是遇到了如下问题&#xff1a; 在Unity里面没法串流调试眼动追踪功能&#xff0c;根本获取不到Device&#xff0c;只能将整个场景build成APK&#xff0c;安装到头盔里&#xff0c;才能在…

枚举(not二分)

前言&#xff1a;这一题似乎用不了二分以及三分&#xff0c;害我写这么久&#xff0c;但是查找下一个元素的时候要用到二分查找 题目地址 #include<bits/stdc.h> using namespace std; #define endl "\n"int n; const int N (int)2e510; vector<int> a;…

OceanBase 中 schema 的定义与应用

背景 经常在OceanBase 的问答社区 里看到一些关于 “schema 是什么” 的提问。 先纠正一些同学的误解&#xff0c; OceanBase 中的 Schema 并不简单的等同于 Database&#xff0c;本次分享将探讨 OceanBase 中的Schema是什么&#xff0c;及一些大家经常遇到的问题。 具体而…

JavaDS —— 图

图的概念 图是由顶点集合以及顶点之间的关系组成的一种数据结构&#xff1a;G &#xff08;V&#xff0c;E&#xff09; 其中 V 表示的是顶点集合 &#xff1a; V { x | x 属于某个数据对象集} 是有穷非空集合 E 叫做边的集合 &#xff1a; E {(x, y) | x, y 属于 V} 或者 …

UE5源码Windows编译、运行

官方文档 Welcome To Unreal Engine 5 Early Access Learn what to expect from the UE5 Early Access program. 链接如下&#xff1a;https://docs.unrealengine.com/5.0/en-US/Welcome/#gettingue5earlyaccessfromgithub Step 0&#xff1a;找到UE5源码 直接先上链接 https…

MySQL原理之UUID主键分析,插入或更新语法分析

文章目录 1 MySQL不能用UUID做主键1.1 前言1.2 mysql和程序实例1.2.1 准备工作1.2.2 开始测试1.2.3 程序写入结果1.2.4 效率测试结果 1.3 使用uuid和自增id的索引结构对比1.3.1 自增id1.3.2 uuid 1.4 自增id缺点1.5 雪花算法 2 插入或更新2.1 on duplicate key2.1.1 定义2.1.2 …

24年蓝桥杯及攻防世界赛题-MISC-3

21 reverseMe 复制图片&#xff0c;在线ocr识别&#xff0c;https://ocr.wdku.net/&#xff0c;都不费眼睛。 22 misc_pic_again ┌──(holyeyes㉿kali2023)-[~/Misc/tool-misc/zsteg] └─$ zsteg misc_pic_again.png imagedata … text: “$$KaTeX parse error: Undefined…

python基础(1)pyenv安装和对Django使用

pyenv安装 pyenv主要针对类 Unix 系统&#xff08;如 Linux、macOS&#xff09;用户&#xff0c;pyenv-win 是专为 Windows 开发的 pyenv 版本&#xff0c;允许您在不使用 WSL 的情况下管理多个 Python 版本和虚拟环境。 建议Git Bash&#xff1a; Powershell或Git Bash&…