Qt_线程介绍与使用
目录
1、QThread常用API
2、Qt线程安全
3、使用线程QThread
4、connect函数的第五个参数
5、Qt互斥锁
5.1 QMutexLocker
6、条件变量
7、信号量
结语
前言:
线程是应用程序开发非常重要的概念,在Qt中,用QThread类来实现多线程,将线程有关的各种函数都封装到这个类中(包括线程执行函数),方便通过该类对线程进行控制。若要创建一个线程执行某些任务,则需要自定义一个继承QThread的类,重写线程执行函数让该线程执行我们期望的任务。
1、QThread常用API
使用QThread创建一个线程对象后,需要调用相关API来操作线程,比如启动线程,等待线程等操作以及获取线程信息,这些成员函数的介绍如下:
run() | 线程的执行函数,即线程所执行的任务,通过start函数间接调用他 |
start() | 真正意义上的创建线程,调用此函数才能让线程执行run函数的内容 |
currentThread() | 返回⼀个指向管理当前执行线程的QThread对象的指针 |
isRunning() | 如果线程正在运行返回true,否则返回false |
sleep() / msleep() / usleep() | 使线程休眠,单位为秒 / 毫秒 / 微秒 |
wait() | 线程等待,调用此函数的线程会阻塞在此函数处,若线程的run函数调用结束或者线程没有被启动,wait返回true。如果等待超时,此函数将返回false |
terminate() | 终止线程的执行 |
finished() | 当线程结束时会发出该信号 |
2、Qt线程安全
线程安全指的是多个线程修改共享资源时会发生意料之外的错误,而Qt中的共享资源毫无疑问是界面本身,而对界面的修改默认是在主线程,如果我们新建线程并对界面进行修改就会引发线程安全问题,因此Qt中严格规定新建的线程不能对界面进行修改!
若需要使用线程对界面进行修改,则采用信号与槽的方式,线程向主线程发送信号,主线程连接该信号与槽,在槽函数中进行对界面的修改,因为槽函数的执行是在主线程,因此不会出现线程安全问题。如下图:
3、使用线程QThread
用QLabel模拟一个时间表,首先在界面上创建一个按钮(pushbutton)和一个标签(label),按钮的作用是让线程开始执行任务,并将任务的结果打印到标签中,至此模拟出时间表。界面设计如下:
1、首先要实现上述功能,必须先创建一个继承自QThread的类,点击新建项目选择新建类:
2、然后自定义类名,并让该类继承QThread,如下:
3、创建完毕后会自动生成相关的文件和代码,我们需要做的是重写run函数,让该线程能够执行我们期望的任务,并且还要给该类自定义一个信号,目的是通过发送信号间接修改界面,mythread.h文件代码如下:
#ifndef MYTHREAD_H
#define MYTHREAD_H#include <QWidget>
#include <QThread>class mythread : public QThread
{Q_OBJECT
public:mythread();void run();//重写run函数signals:void mythread_signal(QString time);//自定义的信号
};#endif // MYTHREAD_H
mythread.cpp文件代码如下:
#include "mythread.h"
#include <QTime>
#include <QDebug>mythread::mythread()
{}void mythread::run()
{while(1){QString time = QTime::currentTime().toString("hh:mm:ss");qDebug()<<time;emit mythread_signal(time);//将时间传过去sleep(1);//每过一秒更新一次}}
4、上述线程的工作已经完成,现在需要在主线程中实例化线程对象,并在主线程中调用线程的start函数让线程跑起来,可以让按钮完成启动线程的工作,并且实现线程信号对应的槽函数,在该槽函数中进行对label的文本设置,widget.h文件代码如下:
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <mythread.h>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();void thread_slot(QString time);//线程信号的槽函数private:mythread thread;//要想启动线程,必须创建线程对象Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp代码如下:
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(&thread,&mythread::mythread_signal,this,&Widget::thread_slot);
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{thread.start();//启动线程
}void Widget::thread_slot(QString time)
{ui->label->setText(time);//设置文本
}
运行结果:
至此实现了使用线程将label标签设置为时间表的功能。整个程序的逻辑:点击按钮启动线程的start函数->线程开始执行run函数并不断的发送信号->主线程收到线程的信号后执行对应的槽函数实现label文本的更新。
4、connect函数的第五个参数
connect函数是用于连接信号与槽的,connect函数第五个参数为Qt::ConnectionType,用于指定信号与槽的连接类型,主要影响槽函数的执行逻辑,一般在多线程的情况下才会用到第五个参数。Qt::ConnectionType提供了以下五种方式:
Qt::AutoConnection | 在Qt中,会根据信号和槽函数是否处于统一线程自动选择连接类型。如果信号和槽函数在同⼀线程中,就会Qt:DirectConnection类型;如果它们位于不同的线程中,则使用Qt::QueuedConnection类型。 |
Qt::DirectConnection | 当信号发出时,槽函数会立即在同⼀线程中执行,可以理解为就是简单的一次函数调用。 |
Qt::QueuedConnection | 当信号发出时,槽函数会被插⼊到接收对象所属的线程的事件队列中,等待下⼀次事件循环时执行。 |
Qt::BlockingQueuedConnection | 与Qt:QueuedConnection类似,但是发送信号的线程会被阻塞,直到槽函数执行完毕。 |
Qt::UniqueConnection | 这是⼀个标志,可以使用位运算和上述任何⼀种连接类型组合使用。 |
5、Qt互斥锁
讲到多线程就自然离不开互斥锁的使用,由于多线程很容易导致线程安全问题,因此使用互斥锁限制多个线程对共享资源的访问。在Qt中,互斥锁主要是通过QMutex类来实现。互斥锁的使用如下。
1、首先创建一个继承QThread的类thread,并在该类中创建一把锁和一个静态变量,thread.h文件代码如下:
#ifndef THREAD_H
#define THREAD_H#include <QWidget>
#include <QThread>
#include <QMutex>class mythread : public QThread
{Q_OBJECT
public:mythread();void run();static int num;//让多个线程对该值进行++
private:static QMutex mutex;//让线程看到同一把锁
};#endif // THREAD_H
2、定义run函数,thread.cpp代码如下:
#include "thread.h"int mythread::num = 0;
QMutex mythread::mutex;mythread::mythread()
{}void mythread::run()
{for(int i = 0;i<100000;i++){mutex.lock();num++;mutex.unlock();}
}
3、在widget.cpp中创建两个线程,这两个线程同时对num进行++操作,最终观察num的值,代码如下:
#include "widget.h"
#include "ui_widget.h"
#include "thread.h"
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);mythread t1;mythread t2;t1.start();t2.start();t1.wait();t2.wait();qDebug()<<mythread::num;
}Widget::~Widget()
{delete ui;
}
运行结果:
此时结果是预期的,因为对访问共享资源进行加锁限制,如果上述代码没有加锁,结果如下:
从这里也可以看到加锁的必要性。
5.1 QMutexLocker
QMutexLocker是QMutex的辅助类,使用RAII(Resource Acquisition Is Initialization)方式 对互斥锁进行自动上锁和解锁的操作。具体来说,他会在被创建时自动上锁,在作用域结束后自动释放锁,避免开发者忘记解锁导致的死锁等问题。
将上述代码的上锁解锁操作用QMutexLocker代替:
#include "thread.h"int mythread::num = 0;
QMutex mythread::mutex;mythread::mythread()
{}void mythread::run()
{for(int i = 0;i<100000;i++){QMutexLocker locker(&mutex);//创建时自动加锁//mutex.lock();num++;//mutex.unlock();}//出了作用域后自动结束
}
运行结果:
6、条件变量
在Qt中,QWaitCondition类表示条件变量,条件变量是作用是让线程实现同步机制,线程同步是为了让所有线程申请到锁的能力是一样的,上述例子中,线程t1和线程t2虽然实现了互斥,但是不具备同步机制,可以通过观察执行线程的地址来判断申请锁的能力,如下图:
添加条件变量后,节选widget.cpp代码:
#include "widget.h"
#include "ui_widget.h"
#include "thread.h"
#include <QDebug>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);mythread t1;mythread t2;t1.start();t2.start();for(int i = 0;i<1000;i++){_sleep(1);mythread::cond.wakeAll();//唤醒条件变量}t1.wait();t2.wait();qDebug()<<mythread::num;}Widget::~Widget()
{delete ui;
}
测试结果:
可以发现两个线程依次有序的对共享资源进行++操作。
7、信号量
信号量类似于加强版互斥锁,不仅能完成上锁和解锁操作,而且还可以限制访问共享资源的线程数量。Qt中用QSemaphore类来表示信号量,用于控制同时访问共享资源的线程数量,测试代码如下:
#include "thread.h"
#include <QDebug>
#include <QSemaphore>int mythread::num = 0;
QMutex mythread::mutex;
QWaitCondition mythread::cond;//初始化条件变量QSemaphore QS(1);//信号量为1,表示只能有一个线程申请到信号量mythread::mythread()
{}void mythread::run()
{for(int i = 0;i<1000;i++){QS.acquire();//尝试获取信号量,若已满则阻塞//QMutexLocker locker(&mutex);//cond.wait(&mutex);//条件变量等待//mutex.lock();num++;qDebug()<<this;//mutex.unlock();QS.release();//释放信号量}
}
结语
以上就是关于Qt线程介绍与使用,在Qt中使用线程最为重要的就是不能直接在线程中对界面进行修改,并且Qt线程采用的是继承的思想,和C++中的线程库采用回调的方式有些不同。线程的其他相关概念都相差无几。
最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!
相关文章:

Qt_线程介绍与使用
目录 1、QThread常用API 2、Qt线程安全 3、使用线程QThread 4、connect函数的第五个参数 5、Qt互斥锁 5.1 QMutexLocker 6、条件变量 7、信号量 结语 前言: 线程是应用程序开发非常重要的概念,在Qt中,用QThread类来实现多线程&a…...
通讯方面的数据,人工智能 机器学习的时候,因为数字都接近于一,数据归一化的一种方法,做了一个简化版本的Z-score标准化
这个表达式实现了一种形式的数据归一化,它将张量x中的每个元素除以x的标准差的估计值。这种处理方式可以使得变换后的数据具有单位标准差(假设数据已经是零均值或者在计算过程中考虑了均值)。具体来说,它是基于以下步骤进行的&…...
python itertools模块介绍
itertools 是 Python 内建的一个高效处理迭代器的模块,提供了创建复杂迭代器的函数工具。它包含一系列用于迭代、组合、排列、过滤等功能的迭代器构建工具,常用于数据处理和算法设计。下面是 itertools 模块中一些常见的函数介绍: 1. 无限迭…...
【分布式微服务云原生】5分钟深入剖析Kafka:Leader与Follower分区的秘密及负载均衡的艺术
深入剖析Kafka:Leader与Follower分区的秘密及负载均衡的艺术 摘要: Apache Kafka作为当前最流行的分布式流处理平台之一,其内部的分区机制和消费者组的负载均衡策略是实现高吞吐量和高可靠性的关键。本文将深入探讨Kafka中Leader分区与Follo…...

在线代码编辑器
在线代码编辑器 文章说明前台核心代码后台核心代码效果展示源码下载 文章说明 采用Java结合vue3设计实现的在线代码编辑功能,支持在线编辑代码、运行代码,同时支持导入文件,支持图片识别,支持复制代码,可将代码导出为图…...
深入了解 MPlayer:Linux 系统中的多功能多媒体播放器
文章目录 深入了解 MPlayer:Linux 系统中的多功能多媒体播放器一、MPlayer 的安装二、MPlayer 的基本使用三、MPlayer 音频功能详解1. 支持的音频格式2. 调整音频输出设备3. 使用音频滤镜和效果4. 音频输出格式转换5. 多声道与环绕声支持6. 音频控制:播放…...

Netty系列-7 Netty编解码器
背景 netty框架中,自定义解码器的起点是ByteBuf类型的消息, 自定义编码器的终点是ByteBuf类型。 1.解码器 业务解码器的起点是ByteBuf类型 netty中可以通过继承MessageToMessageEncoder类自定义解码器类。MessageToMessageEncoder继承自ChannelInboundHandlerAdap…...

OpenHarmony标准系统上实现对rk系列芯片NPU的支持(npu使用)
在上篇文章中,我们学习了移植rk的npu驱动到OpenHarmony提供的内核。本文我们来学习如何在OpenHarmony标准系统rk系列芯片如何使用npu OpenHarmony RK系列芯片运行npu测试用例 在移植npu驱动到OpenHarmony之后,来运行npu样例进行简单测试 1.O 测试准备…...
大表性能优化的关键技术
1 引言 在现代企业应用中,随着数据量的不断增长,大表的性能优化成为数据库管理的重要环节。本文将探讨大表性能优化的关键技术,包括索引优化、查询优化、分区分表、读写分离以及缓存策略等方面。通过综合运用这些技术,可以显著提升大表的处理效率和响应速度,确保系统的稳…...

广联达 Linkworks办公OA Service.asmx接口存在信息泄露漏洞
漏洞描述 广联达科技股份有限公司以建设工程领域专业应用为核心基础支撑,提供一百余款基于“端云大数据”产品/服务,提供产业大数据、产业新金融等增值服务的数字建筑平台服务商。广联达OA存在信息泄露漏洞,由于某些接口没有鉴权,…...
如何成为成功的AI产品经理:经验与策略分享
引言 随着人工智能(AI)技术的迅猛发展,AI产品经理(AI PM)的角色变得越来越重要。Google AI产品负责人Marily Nika在最近的一次播客中分享了她在AI产品管理领域的宝贵经验和见解。本文将整理并总结她的核心内容,帮助有志于进入AI PM领域的人士了解如何准备、所需的核心技…...

spring loCDI 详解
文章目录 一、IoC & DI 基本知识1.1 IoC 的基本概念:1.2 IoC 的优势:1.3 DI 介绍: 二、IoC 详解2.1 Spring 容器:2.2 被存储 Bean 的命名约定:2.3 Bean 的存储方式:2.3.1 五大类注解:2.3.1.…...
遇到 Docker 镜像拉取失败的问题时该如何解决
遇到 Docker 镜像拉取失败的问题时,可以按照以下步骤进行排查和解决: 1. 检查网络连接 确保你的计算机可以访问互联网。尝试 ping 通 Docker Hub 或其他镜像仓库的域名: ping hub.docker.com2. 检查 Docker 服务状态 确保 Docker 服务正在…...

【C/C++】错题记录(三)
题目一 题目二 题目三 题目四 题目五 题目六 题目七??? 题目八 这道题主要考查对数据类型和位运算的理解与运用。 分析选项 A: *((unsigned char *)(&number) 1)0xcd; 这里将 number 的地址强制转换为 unsigned char* 类型&a…...
深入理解Web浏览器与服务器的连接过程
目录 1. 域名解析:找到地址 2. TCP连接:建立通信 3. HTTP请求:点菜 4. 服务器处理请求:厨房做菜 5. HTTP响应:上菜 6. 客户端接收响应:品尝美食 7. 关闭TCP连接:吃完离开 8. 持久连接&a…...

深入解析 https
我的主页:2的n次方_ 1. 背景介绍 在使用 http 协议的时候是不安全的,可能会出现运营商劫持等安全问题,运营商通过劫持 http 流量,篡改返回的网页内容,例如广告业务,可能会通过 Referer 字段 来统计是…...
NP-hard问题
一、前置知识 1.多项式 多项式是由变量(如x、y等)和系数通过有限次的加、减、乘运算得到的表达式。例如3x^22x 1就是一个关于(x)的多项式 2.时间复杂度 时间复杂度是用来衡量算法运行效率的一个指标。它描述了算法运行时间随着输入规模增长而增长的量…...

【Nacos架构 原理】内核设计之Nacos通信通道
文章目录 Nacos通信通道 (长链接)现状背景场景分析配置服务 长链接核心诉求功能性诉求负载均衡连接生命周期 Nacos通信通道 (长链接) 现状背景 Nacos 1.X 版本 Config/Naming 模块各自的推送通道都是按照自己的设计模型来实现的…...

【单片机】单片机map表详细解析
1、RO Size、RW Size、ROM Size分别是什么 首先将map文件翻到最下面,可以看到 1.1 RO Size:只读段 Code:程序的代码部分(也就是 .text 段),它存放了程序的指令和可执行代码。 RO Data:只读…...

考研笔记之操作系统(三)- 存储管理
操作系统(三)- 存储管理 1. 内存的基础知识1.1 存储单元与内存地址1.2 按字节编址和按字编址1.3 指令1.4 物理地址和逻辑地址1.5 从写程序到程序运行1.6 链接1.6.1 静态链接1.6.2 装入时动态链接1.6.3 运行时动态链接 1.7 装入1.7.1 概念1.7.2 绝对装入1…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

接口自动化测试:HttpRunner基础
相关文档 HttpRunner V3.x中文文档 HttpRunner 用户指南 使用HttpRunner 3.x实现接口自动化测试 HttpRunner介绍 HttpRunner 是一个开源的 API 测试工具,支持 HTTP(S)/HTTP2/WebSocket/RPC 等网络协议,涵盖接口测试、性能测试、数字体验监测等测试类型…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...

新版NANO下载烧录过程
一、序言 搭建 Jetson 系列产品烧录系统的环境需要在电脑主机上安装 Ubuntu 系统。此处使用 18.04 LTS。 二、环境搭建 1、安装库 $ sudo apt-get install qemu-user-static$ sudo apt-get install python 搭建环境的过程需要这个应用库来将某些 NVIDIA 软件组件安装到 Je…...