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…...
vim/vi常用命令大全
启动和退出Vim 命令/操作作用vim启动Vimvim filename直接打开指定的文件命令模式下,输入 :q退出,q!强制退出:wq保存并退出:wq!保存并强制退出vim中按下a进入编辑模式Esc退出编辑模式进入命令模式new创建新窗口close关闭窗口 光标移动 命令/操作作用h、…...
什么是大语言模型,一句话解释
定义 先说语言模型(Language Model)旨在建模词汇序列的生成概率,提升机器的语言智能水平,使机 器能够模拟人类说话、写作的模式进行自动文本输出。 白话:语言模式是一种解决机器与人类交流的手段,机器人与…...
【数据库】 MongoDB 撤销用户的角色和权限
在 MongoDB 中,撤销用户的角色和权限是一项重要的管理任务,确保用户仅能访问和操作他们需要的数据。以下是如何撤销用户的角色和权限的详细步骤。 1. 使用 MongoDB Shell 撤销角色 1.1 修改用户角色 要撤销用户的角色,可以使用 updateUser…...
vue2接入高德地图实现折线绘制、起始点标记和轨迹打点的完整功能(提供Gitee源码)
目录 一、申请密钥 二、安装element-ui 三、安装高德地图依赖 四、完整代码 五、运行截图 六、官方文档 七、Gitee源码 一、申请密钥 登录高德开放平台,点击我的应用,先添加新应用,然后再添加Key。 如图所示填写对应的信息&…...
【重学 MySQL】四十六、创建表的方式
【重学 MySQL】四十六、创建表的方式 使用CREATE TABLE语句创建表使用CREATE TABLE LIKE语句创建表使用CREATE TABLE AS SELECT语句创建表使用CREATE TABLE SELECT语句创建表并从另一个表中选取数据(与CREATE TABLE AS SELECT类似)使用CREATE TEMPORARY …...
WPS在表格中填写材料时,内容过多导致表格不换页,其余内容无法正常显示 以及 内容过多,导致表格换页——解决方法
一、现象 1,内容过多导致表格不换页,其余内容无法正常显示 2,内容过多,导致表格换页 二、解决方法 在表格内右击,选择表格属性 在菜单栏选择行,勾选允许跨页断行,点击确定即可 1࿰…...
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-01
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-01 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-01目录1. Beyond Text-to-Text: An Overview of Multimodal and Generative Artificial Intelligence for Education Using Topi…...
第一弹:C++ 的基本知识概述
文章目录 知识点 1:C 的概述1. C的特征2. C 程序的编辑、编译和执行3. 第一个 C 源程序4. 面向对象程序设计思想4.1 面向对象程序设计思想初始4.2 面向对象程序设计思想的核心 知识点 2:C 对 C 的扩展1. 作用域访问运算符 ::1.1 全局变量和局部变量1.2 作…...
在职场,没人告诉你的人情世故
职场中,想要过得游刃有余,就必须懂一些人情世故和处事原则。今天,给大家分享个人认为非常重要的5点人情世故,希望能帮你在职场里少吃点亏、多份从容。 01 不要空口道谢 在职场中,别人帮了你,口头道谢是基…...
激光切割机适用材质有哪些
激光切割机是一种利用激光束对各种材料进行高精度、高速度切割的机器设备。其适用材质广泛,包括但不限于以下两大类: 一、金属材料 不锈钢:激光切割机较容易切割不锈钢薄板,使用高功率YAG激光切割系统,切割不锈钢板的…...
佛山顺德容桂做网站的公司/win7优化大师
着互联网的不断发展和逐渐普及,各行各业也纷纷选择了上云之路,腾讯云数据库致力于运用领先技术,助力企业上云,分布式数据库TDSQL就是部署在腾讯云上的一款具备强一致高可用、全球部署架构、分布式水平扩展、高性能、企业级安全等特…...
从化专业做网站/品牌营销推广策划方案
转载地址:http://cnodejs.org/topic/58f376fec749f63d48fe9548 今天就要给大家安利一下在VScode上进行断点调试的方法。 VScode除了out-of-box支持JavaScript和TypeScript,还支持Node.js调试,简直就是为前端工程师而生的,对不对……...
网站运营需要哪些技术/海淀区seo引擎优化多少钱
Lecture 8 Additional Storage Linux上所有的存储设备都需要有一个设备文件对应,这些设备文件位于/dev/目录下。 USB,SCSI,SATA,iscsi设备以sd开头,IDE以hd开头。表示方式:/dev/sd[a-z]N。 这些设备文件由u…...
个人博客网页模版/爱站网seo
Luogu4983 忘情 定义序列 \(x_1,\ x_2,\ \cdots,\ x_n\) 的值为 \(\frac{((\displaystyle\sum_{k1}^nx_k\times \bar x) \bar x)^2}{\bar x^2}\) 给定一段序列 \(a_1,\ a_2,\ \cdots,\ a_n\) ,将它分成 \(m\) 段,使每段值的和最小,求这个最小…...
有源码怎么在本地网站搭建/bt磁力搜索器
求职经验分享者Crydi:猴子数据分析学员成功入职金融行业数据分析师跳槽薪资提升30%你将收获:1、如何学习准备才能跳槽成功提升薪资?2、如何做一份面试官喜欢的简历?3、如何准备面试,才能让面试官对你刮目相看ÿ…...
怎么请专业拓客团队/关键词优化最好的方法
图像锐化处理的主要目的是突出图像中的细节,像边沿区域,锐化处理与图像在该点的突变程度有关。图像微分增强了边缘与其他突变的信息,并削弱了灰度变化缓慢的信息。以灰度图像为例,它的理论基础是这样的,如果出现一个边…...