Qt中的多线程

news2025/2/27 7:20:07

Qt中有多种方法实现多线程:

  1. QThread
  2. QThreadPool和QPunnable(重用线程)
  3. Qt Concurrent
  4. WorkerScript(QML中的线程)

QThread 在上两篇文章中已经解释了,这里就不再赘述。 

QThreadPoo和QRunnable(实现的多线程)

QRunnable:

QRunnable 类是一个接口,用于表示需要执行的任务或代码段,由 run 函数的重新实现表示。

autoDelete()返回是否自动删除
setAutoDelete()设置自动删除(thue)

设置自动删除的话,在QThreadPool中会自动删除QRunnable

 使用的注意事项:

 启用自动删除时,使用相同的 QRunnable 多次调用 QThreadPoool::start() 会产生争用条件,不建议这样做。

QRunnable和QThread的区别:

  

 QThreadPool(线程池)

顾名思义,是有很多线程的池子, 可以管理池子中的线程。

线程池的优点:

  1. 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。 线程是稀缺资源,使用线程池可以进行统一的分配、调优和监控。
  4.   

常用函数: 

setMaxThreadCount()  

设置线程池中最大的线程数,默认:QThread::idealThreadCount
setExpiryTimeout(int)将超时xx毫秒未使用的线程视为已过期并将退出,已在运行的线程没有影响。建议在创建线程池后立即设置。
setStackSize()       设置线程池工作线程的堆栈大小,当线程池创建新线程时使用, 默认值为 0
clear()清除队列中未开启的可运行项
globalInstance()返回一个全局QThreadPool实例
reserveThread()保留一个线程( 此函数将始终增加活动线程数)
releaseThread()释放reserveThread()保留的线程
start(QRunnable,int)开启一个线程,可以设置优先级
tryStart()尝试开启一个线程
tryTake()尝试从队列中删除指定的尚未启动的可运行对象(谨慎使用)
waitForDone(int)等待所有线程退出最多毫秒毫秒,并从线程池中删除所有线程。如果删除了所有线程返回true
activeThreadCount()当前活跃的线程数

启动QRunnable线程的方法: 

  1. 使用QThreadPool::globalInstance()->start()启动   (全局)
  2. 创建一个QThreadPool 对象,然后通过该对象启动

Runnable类:

#ifndef RUNNABLE_H
#define RUNNABLE_H
#include<QThread>
#include<QDebug>
#include<QRunnable>

class Runnable:public QRunnable
{
public:
    explicit Runnable()//构造函数
    {

    }
    ~Runnable()//析构函数
    {

    }
    void run()//重写run函数
    {
        qDebug()<<"Runnable中的run函数";
        qDebug()<<"thread to run():"<<QThread::currentThreadId();
    }
};
#endif // RUNNABLE_H

1.使用QThreadPool::globalInstance()->start()启动   (全局)

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    qDebug()<<"主线程号:"<<QThread::currentThread();
    Runnable *able=new Runnable;
    QThreadPool::globalInstance()->start(able);

}

  

2.创建一个QThreadPool 对象,然后通过该对象启动

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    qDebug()<<"主线程号:"<<QThread::currentThread();
    Runnable *able=new Runnable;
    //QThreadPool::globalInstance()->start(able);
    QThreadPool pool;
    pool.start(able);

}

  

 

线程池函数的使用

    QThreadPool pool;
    qDebug()<<"本机的线程数"<<QThread::idealThreadCount();
    qDebug()<<"线程池中的线程数"<<pool.maxThreadCount();
    pool.setMaxThreadCount(10);//设置线程池中最大的线程数
    pool.setExpiryTimeout(40000);//如果线程40000毫秒,40秒未使用,使它过期
    Runnable *runnable=new Runnable;
    pool.reserveThread();//保留一个线程
    pool.start(runnable);//开启线程
    qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();
    pool.releaseThread();//释放那个保留的线程
    qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();

    

QRunnable释放内存问题:

QRunnable可以执行完释放内存:需要将autoDelete()设置为true,默认为true。

#ifndef RUNNABLE_H
#define RUNNABLE_H
#include<QThread>
#include<QDebug>
#include<QRunnable>

class Runnable:public QRunnable
{
public:
    explicit Runnable()//构造函数
    {

    }
    ~Runnable()//析构函数
    {
        qDebug()<<__FUNCTION__;
    }
    void run()//重写run函数
    {
        qDebug()<<"thread to run():"<<QThread::currentThreadId();
    }
};
#endif // RUNNABLE_H
    QThreadPool pool;
    Runnable *runnable=new Runnable;
    pool.start(runnable);//开启线程

执行完后自动调用析构函数。

  

QRunnable的通信问题:

由于QRunnable是单独的一个类,并没有继承QObject,所以没有connect()函数,所以无法于外界进行通信。

解决该问题的方法:

  • 使用QMetaObject::invokeMethod()
  • 使用多继承的方式,同时继承QRunnable和QObject,但这样的话会使接口混乱(尽量不要使用)

QMetaObject::invokeMethod()的创建方法 :

QMetaObject::invokeMethod(QObject *obj, //使用的类
                            const char *member, //调用函数名
                            Qt::ConnectionType type,//调用类型
                            QGenericReturnArgument ret,//用来存储返回值的类型
                            QGenericArgument val0 = QGenericArgument(nullptr),
                            QGenericArgument val1 = QGenericArgument(), 
                            QGenericArgument val2 = QGenericArgument(), 
                            QGenericArgument val3 = QGenericArgument(),
                            QGenericArgument val4 = QGenericArgument(),
                            QGenericArgument val5 = QGenericArgument(),
                            QGenericArgument val6 = QGenericArgument(), 
                            QGenericArgument val7 = QGenericArgument(), 
                            QGenericArgument val8 = QGenericArgument(),
                            QGenericArgument val9 = QGenericArgument())
                            //最多有10个参数

*obj:需要把使用类传进去

member:函数的名称

type:调用类型:

  1. Qt::DirectConnnect 立即调用该成员函数
  2. Qt::QueuedConnection 一旦应用程序进入主事件循环,就会发送 QEvent 并调用成员。
  3. Qt::BlockQueuedConnection 当前线程将阻塞,直到事件被传递。使用此连接类型在同一线程中的对象之间进行通信将导致死锁。
  4. Qt::AutoConnection 如果 obj 与调用方位于同一线程中,则同步调用成员;否则,它将异步调用该成员。

val0-val9:最多可以有10个参数

传参的方式:使用宏传参

  1. Q_ARG(type,const Type&value)
  2. Q_RETURN_ARG(type, Type&value)
QString S="hellow";
QMetaObject::invokeMethod(obj,              
                         "Text",
                         Qt::DirectConnection,
                         Q_ARG(QString,"pppp"),
                         Q_RETURN_ARG(QString,S));

Runnable函数:

#ifndef RUNNABLE_H
#define RUNNABLE_H
#include "widget.h"
#include<QThread>
#include<QObject>
#include<QDebug>
#include<QRunnable>
#include<QMetaObject>

class Runnable:public QRunnable
{
public:
    explicit Runnable(QObject *obj1):obj(obj1)//构造函数
    {
        

    }
    ~Runnable()//析构函数
    {
        
    }
    void run()//重写run函数
    {  
        QString S="hellow";
        QMetaObject::invokeMethod(obj,"Text",Qt::DirectConnection,Q_ARG(QString,"pppp"),Q_RETURN_ARG(QString,S));
        qDebug()<<"thread to run():"<<QThread::currentThreadId();
    }
    QObject *obj=nullptr;//需要传的对象
};
#endif // RUNNABLE_H

主函数:

主类中创建函数开头需要添加:Q_INVOKABLE(这样才直到调用哪一个函数)

#include "widget.h"
#include "ui_widget.h"
#include"runnable.h"
#include<QThreadPool>
class Rnnable;
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QThreadPool pool;
    Runnable *runnable=new Runnable(this);//传入调用的对象
    pool.start(runnable);//开启线程
}
Q_INVOKABLE void Widget::Text(QString a,QString b)//创建一个函数,名称要相同
{
    qDebug()<<a;
    qDebug()<<b;
}
Widget::~Widget()
{
    delete ui;
}

    

Qt Concurrent(并发)

QtConcurrent命名空间提供高级 API,可以在不使用互斥锁、读写锁、等待条件或信号量等低级线程原语的情况下编写多线程程序。

使用 QtConcurrent编写的程序会根据可用的处理器内核数量自动调整使用的线程数。这意味着编写的应用程序在部署到多核系统时将自动扩展。

如何使用Qt Concurrent:

1.pro文件中添加

QT +=concurrent

2. 添加头文件

#include<QtConcurrent>

 以下是如何执行QtConcurrent的使用方法:

使用QtConcurrent::run()函数执行内容

 函数模型:

//T为模板类型

QFuture<T> QtConCurrent::run(Function)//执行一个函数

QFuture<T> QtConCurrent::run(threadpool,Function)//线程池中,取出线程执行function函数

执行一个函数:

void text()
{
    qDebug()<<"ok";
}
void text1(int a)
{
    qDebug()<<a;
}
void text2(QString s1,QString s2)
{
    qDebug()<<s1+s2;
}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    
    QFuture<void> fu=QtConcurrent::run(text);
    QFuture<void> fu1=QtConcurrent::run(text1,10);
    QFuture<void> fu2=QtConcurrent::run(text2,QString("aa"),QString("bb")");
    fu.waitForFinished();//等待完成
    fu1.waitForFinished();//等待完成
    fu2.waitForFinished();//等待完成
    
    return a.exec();
}

   

通过线程池中的进程执行函数

void text1(int a)
{
    qDebug()<<a;
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QThreadPool pool;
    QFuture<void> fu=QtConcurrent::run(&pool,text1,10);
    fu.waitForFinished();//等待完成
    return a.exec();
}

  

3.调用常量成员函数:

run()函数可以接收成员函数的指针,第一个参数必须是常量引用或指向类实例的指针。

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

4.调用非常量成员函数:

QImage image = ...;
QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels,QImage::InvertRgba);
future.waitForFinished();  


总结:以上三种多线程的特点:

特征QThreadQRunnable和QThreadPoolQtConcurrent::run()

QtConcurrent

(Map,Filter,Reduce)

指定优先级

可运行

事件循环

线程可以通过

信号接收数据更新

可以使用

信号控制螺纹

是(QFutureWatcher)

可以通过

QFuture监控线程

部分是

内置暂停/

恢复/取消功能

适用场景:

生命周期开发任务解决方案
持续运行在另一个线程中重复执行昂贵的操作,其中线程不需要接收任何信号或事件。直接在QThread::run()的重新实现中编写无限循环。在没有事件循环的情况下启动线程。让线程发出信号以将数据发送回 GUI 线程。
持续运行让一个对象存在于另一个线程中,该线程可以根据请求执行不同的任务和/或可以接收要使用的新数据。子类 一个QObject 以创建工作线程。实例化此工作线程对象和 QThread。将工作线程移动到新线程。通过排队的信号槽连接将命令或数据发送到工作器对象。
一次调用在另一个线程中运行新的线性函数,可以选择在运行期间更新进度。Qt提供不同的解决方案:
  • 将函数放在 QThread::run() 的重新实现中并启动 Qthread。发出信号以更新进度。或
  • 将函数放在 QRunnable::run() 的重新实现中,并将 QRunnable 添加到 QthreadPool 中。写入线程安全变量以更新进度。或
  • 使用 Q他Concurrent() 运行函数。写入线程安全变量以更新进度。
一次调用在另一个线程中运行现有函数并获取其返回值。使用 QtConcurrent::run() 运行函数。让 QFutureWatcher 在函数返回时发出 done() 信号,并调用 QFutureWatcher::result() 来获取函数的返回值。
一次调用使用所有可用核心对容器的所有项执行操作。例如,从图像列表生成缩略图。

使用 Qt Concurrent 的 QtConcurrent::filter() 函数选择容器元素,使用 QtConcurrent::map() 函数对每个元素应用操作。要将输出折叠成单个结果,请改用 QtConcurrent::filteredReduction() 和 QtConcurrent::mappedReduction()。

参考文章:

Thread Support in Qt | Qt 5.15

 Qt线程之QRunnable的使用详解_luoyayun361的博客-CSDN博客_qrunnable 

【QT】继承QRunnable+QThreadPool实现多线程_李春港的博客-CSDN博客_继承qrunnable

Qt并发模块Qt Concurrent的使用_Amnesia Greens的博客-CSDN博客_qtconcurrent 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/361453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot2核心技术-核心功能【05、Web开发】

目录 1、SpringMVC自动配置概览 2、简单功能分析 2.1、静态资源访问 1、静态资源目录 2、静态资源访问前缀 2.2、欢迎页支持 2.3、自定义 Favicon 2.4、静态资源配置原理 3、请求参数处理 0、请求映射 1、rest使用与原理 2、请求映射原理 1、普通参数与基本注解 …

PrivateLoader PPI服务发现RisePro恶意软件窃取分发信息

称为PrivateLoader的按安装付费&#xff08;PPI&#xff09;软件下载器服务正用于恶意软件RisePro的信息窃取。Flashpoint 于 2022 年 12月13日发现了新的窃取者&#xff0c;此前发现了在名为Russian Market的非法网络犯罪市场上使用该恶意软件泄露的“几组日志”。RisePro是一…

因“AI”而“深” 第四届OpenI/O 启智开发者大会高校开源专场25日开启!

中国算力网资源不断开发&#xff0c;开源社区治理及AI开源生态引来众多有才之士参与建设&#xff0c;国家级开放创新应用平台、NLP大模型等高新技术内容逐渐走向科研舞台上聚光灯的中心&#xff0c;新时代的大门缓缓打开。在启智社区&#xff0c;有一群人&#xff0c;他们年纪轻…

BEV感知:DETR3D

3D检测&#xff1a;DETR3D前言MethodImage Feature Extracting2D-to-3D Feature TransformationLoss实验结果前言 在这篇paper&#xff0c;作者提出了一个更优雅的2D与3D之间转换的算法在自动驾驶领域&#xff0c;它不依赖于深度信息的预测&#xff0c;这个框架被称之为DETR3D…

性能测试学习和性能瓶颈分析路线

很多企业招聘都只写性能测试&#xff0c;会使用LR&#xff0c;jmeter工具。其实会使用jmeter和LR进行性能测试还只是性能测试的第一步&#xff0c;离真正的性能测试工程师还很远&#xff0c;笔者也还在路上 .。 性能测试&#xff0c;都是要求测试系统性能&#xff0c;系统自然…

面试中经常被问到的【宏定义】,改变你对【C\C++】中宏定义的认识。

最近遇到挺多宏定义的代码&#xff0c;其实挺烦的&#xff0c;每次看复杂的宏定义看到一半就懵了&#xff0c;今天盘一盘它。本篇设计宏定义的原理、使用方法、使用技巧。 目录 一、宏定义原理 二、宏定义定义复杂功能函数 2.1 定义注册函数 三、宏定义实现条件编译 四、宏…

【OpenCV学习笔记01】- 初步使用OpenCV实现人脸识别

想要使用opencv实现人脸识别&#xff0c;我们需要做这样几步&#xff1a; 1.opencv-python的安装 这里我们使用的python的opencv-python库&#xff0c;在安装opencv-python库之前&#xff0c;我们需要安装numpy, matplotlib。 # 安装指令 # 安装 numpy pip install numpy # …

Chirp-Z变换(线性调频Z变换)原理

Chirp-Z变换&#xff08;Chirp-Z Transform&#xff0c;CZT&#xff09; 采用FFT算法可以很快地计算出全部DFT值&#xff0c;即Z变换在单位圆上的全部等间隔采样值。 在实际情况中&#xff0c;并不需要对整个单位圆的频谱进行分析&#xff0c;例如&#xff0c;对于窄带信号&am…

运动型蓝牙耳机推荐哪款、最新运动蓝牙耳机推荐

提起运动耳机&#xff0c;如今很多运动爱好者和职业教练员们&#xff0c;都会向萌新推荐骨传导运动耳机。骨传导耳机解决了入耳式蓝牙耳机掉落的问题&#xff0c;佩戴相当舒服。骨传导耳机在佩戴过程中解放了双耳&#xff0c;不会因为耳机堵住耳朵&#xff0c;听不到环境音&…

【Spring6】| Spring启示录、Spring概述

目录 一&#xff1a;Spring启示录 1. OCP开闭原则 2. 依赖倒置原则DIP 3. 控制反转IoC 二&#xff1a;Spring概述 1. Spring简介 2. Spring8大模块 3. Spring特点 一&#xff1a;Spring启示录 引言&#xff1a;前面我们已经学习了三层架构&#xff1a;表示层、业务层、…

【工作笔记】syslog,kern.log大量写入invalid cookie错误信息问题

任务描述 错误出现出现过四五次&#xff0c;应该是诊断单元tf卡读写出问题导致下面这条告警一直高频写入到/var/log/下的syslog、kern.log、messages中 Nov 23 06:25:12 embest kernel: omap_hsmmc 48060000.mmc: [omap_hsmmc_pre_dma_transfer] invalid cookie: data->hos…

将maven项目打包成可执行的jar(加入外部依赖)

在有些场景下我们需要将编写的Java工程编译打包成为一个完整的jar包&#xff0c;如果你的项目是使用maven构建的话可以通过以下方法来完成这个打包的过程。添加maven打包插件。在项目的pom.xml配置文件的build标签中添加以下代码&#xff0c;其中 mainClass 属性需要替换成你项…

多种调度模式下的光储电站经济性最优储能容量配置分析(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

安装MQTT Server遇到报错“cannot verify mosquitto.org‘s certificate”,该如何解决?

MQTT是基于发布/订阅的轻量级即时通讯协议&#xff0c;很适合用于低带宽、不稳定的网络中进行远程传感器和控制设备通讯等操作中。在我们的软件研发中&#xff0c;也经常使用MQTT协议进行消息通信等。今天来和大家分享一些关于在安装MQTT Server中遇到的疑难问题及解决思路。当…

为什么阳康后,感觉自己变傻了?

不少人在阳康后出现脑力下降的情况&#xff0c;好像脑子里被雾笼罩。脑雾并不是新名词&#xff0c;已经存在了十几年。以前慢性疲劳综合征患者和脑震荡患者会用它来形容自己的症状。脑雾其实是认知障碍&#xff0c;它可由多种原因引起。比如过度劳累、长期酗酒、缺乏睡眠、久坐…

Semi-supervised(半监督)布料缺陷检测实战

数据及源码链接见文末 1.任务目标和流程概述 对于常规的缺陷检测,常常需要我们准备好数据,使用分割或者检测的方法选择模型,进行训练。但是有一个问题。在日常生产中,我们接触到的往往都是正常的,缺陷数据往往很难收集,更何况我们还要打标签。我们能不能通过训练正常数据…

独立搭建 handle server

本节主要介绍,如何搭建一个与 GHR隔离的 handle sever,不与外界有任何连通。 下载文件 访问地址下载最新版:http://www.handle.net/download_hnr.html 这里以 9.3.0 版本作为讲解 解压服务端,解压客户端 # 解压 tar -xzvf handle-9.3.0-distribution.tar.gz# 到目录下 …

NestJS学习:图片上传、下载

参考 大神的这两篇文章讲的很详细&#xff0c;这里自己也来试一下 小满nestjs&#xff08;第十三章 nestjs 上传图片-静态目录&#xff09; 小满nestjs&#xff08;第十四章 nestjs 下载图片&#xff09; 上传图片 安装包 需要&#xff1a;multer 和 nestjs/platform-expre…

深入理解Java的Lambda原理

1、前言 Lambda函数的概念其实有很久远的历史了&#xff0c;在Lisa&#xff0c;C#中早有实现。且近年来&#xff0c;开发者对语言的表现力有了更高的要求&#xff0c;Java也在JDK 1.8 中引入了Lambda函数这一概念。虽然截止到写下这段文字的一刻已经过去七年之久&#xff0c;但…

Mybatis框架详解(全)

目录 MyBatis简介 MyBatis整体架构及运行流程 1.数据源配置文件 2.Sql映射文件 3.会话工厂与会话 4.运行流程 mybatis实现增删改查 Mybatis的获取参数的方式 mapper中自定义映射 mybatis注解开发 mybatis缓存 mybatis分页插件 MyBatis简介 MyBatis 是一款优秀的持久…