Qt_线程介绍与使用

news2024/9/28 23:37:29

目录

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_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    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++中的线程库采用回调的方式有些不同。线程的其他相关概念都相差无几。

        最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!   

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

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

相关文章

Spring项目中的统一结果返回

本文介绍的是通过Spring提供的接口进行结果统一封装&#xff0c;指的是成功返回的结果&#xff0c;不包含异常或者错误情况&#xff08;这一块移步到统一异常处理&#xff09;。另一种统一结果返回的方式&#xff0c;就是手动让每个接口的返回中类型都相同&#xff0c;这种方法…

ant design vue组件中table组件设置分组头部和固定总结栏

问题&#xff1a;遇到了个需求&#xff0c;不仅要设置分组的头部&#xff0c;还要在顶部有个统计总和的栏。 分组表头的配置主要是这个&#xff0c;就是套娃原理&#xff0c;不需要展示数据的直接写个title就行&#xff0c;需要展示数据的字段才需要详细的配置属性。 const co…

实现简易 vuedraggable 的拖拽排序功能

一、案例效果 拖拽计数4实现手动排序 二、案例代码 <draggable:list"searchResult.indicator":group"{ name: indicators }"item-key"field"handle".drag-handle-icon"><divclass"field-item"v-for"(item…

YOLOv8+注意力机制+PyQt5玉米病害检测系统完整资源集合

资源包含可视化的玉米病害检测系统&#xff0c;基于最新的YOLOv8注意力机制训练的玉米病害检测模型&#xff0c;和基于PyQt5制作的可视玉米病害系统&#xff0c;包含登陆页面和检测页面&#xff0c;该系统可自动检测和识别图片或视频当中出现的七类玉米病害&#xff1a;矮花叶病…

Java每日面试题(mysql优化)(day14)

目录 连接查询MySQL常用函数汇总SQL Select 语句的执行顺序数据库三范式MyISAM 存储引擎 与 InnoDB 引擎区别索引索引的优缺点索引的分类索引结构B树与B树的区别索引失效的几种情况 数据库锁MySql 优化 连接查询 MySQL常用函数汇总 SQL Select 语句的执行顺序 数据库三范式 第一…

物联网网络中集中式与分布式SDN环境的比较分析

论文标题&#xff1a;Comparative Analysis of Centralized and Distributed SDN Environments for IoT Networks 中文标题&#xff1a;物联网网络中集中式与分布式SDN环境的比较分析 作者信息&#xff1a; Khirota G. Yalda, Diyar J. Hamad, Nicolae Tapus罗马尼亚布加勒斯…

科研绘图系列:R语言树结构聚类热图(cluster heatmap)

文章目录 介绍加载R包导入数据数据预处理画图修改图形导出数据系统信息介绍 热图结合树结构展示聚类结果通常用于展示数据集中的模式和关系,这种图形被称为聚类热图或层次聚类热图。在这种图中,热图部分显示了数据矩阵的颜色编码值,而树结构(通常称为树状图或聚类树)则显…

【高中数学/函数/零点】求分段函数f(x)=x^2-4x+2(x>=1) f(x)=|lg(1-x)| (x<1)的零点个数

【问题】 已知分段函数f(x)x^22x(当x<0时)&#xff0c;f(x)|lgx|(当x>0时)&#xff0c;则函数g(x)f(1-x)-1的零点个数为几个&#xff1f; 【来源】 《高考数学极致解题大招》P137 变式训练第1题 中原教研工作室编著 【解答】 f(1-x)-10即f(1-x)1 当1-x>0,即x&l…

遍历9个格子winmine!StepBlock和遍历8个格子winmine!StepBox的对决

遍历9个格子winmine!StepBlock和遍历8个格子winmine!StepBox的对决 第一部分&#xff1a;windbg调试记录。 0: kd> g Breakpoint 10 hit winmine!DoButton1Up: 001b:0100390e a130510001 mov eax,dword ptr [winmine!xCur (01005130)] 0: kd> kc # 00 winmine…

基于JAVA+SpringBoot+Vue的健身房管理系统1

基于JAVASpringBootVue的健身房管理系统1 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345; 哈喽…

element ui中当el-dialog需要做全屏时,.fullscreen样式修改问题

element ui 饿了么UI中el-dialog样式修改问题 场景解决方法就是&#xff1a;去掉底部样式中的scoped,然后再进行页面级样式的更改即可。 场景 最近在使用element-ui时&#xff0c;使用到了弹窗组件&#xff1a; element-ui 官网链接地址&#xff1a; element-ui 官网链接地址…

C语言指针系列1——初识指针

祛魅&#xff1a;其实指针这块儿并不难&#xff0c;有人说难只是因为基础到进阶没有处理好&#xff0c;大家要好好跟着一步一步学习&#xff0c;今天我们先来认识一下指针 指针定义&#xff1a;指针就是内存地址&#xff0c;指针变量是用来存放内存地址的变量&#xff0c;在同一…

Java线程的三大特性

原子性: 线程任务不可再分。 int i 1; i; 原子类 AtomicXXX 可见性: 线程之间的操作是互相不可见的。 volatile //线程A如果&#xff0c;flag为true&#xff0c;就运行打印语句 A: trueThread t1 new Thread(() -> {while (true) {if (flag) {System.out.println(&q…

卷积神经网络-迁移学习

文章目录 一、迁移学习1.定义与性质2.步骤 二、Batch Normalization&#xff08;批次归一化&#xff09;三、ResNet网络1.核心思想2.残差结构&#xff08;1&#xff09;残差块&#xff08;2&#xff09;残差结构类型 四、总结 一、迁移学习 迁移学习&#xff08;Transfer Lear…

zabbix基本概念与组件

文章目录 一、zabbix简介二、​​​​​​​zabbix构成三、​​​​​​​zabbix监控对象四、​​​​​​​zabbix常用术语五、 Zabbix 6.0 新特性1.Zabbix server高可用防止硬件故障或计划维护期的停机2.Kubernetes系统从多个维度采集指标 六、zabbix 工作原理1、主动模式2、…

操作配置笔记

一、检查&#xff1a; 1.查看当前配置 display current-configuration 2.查看路由表 display ip routing-table 3.查看当前配置情况 display this 4.查看当前设备版本 display version 5.查看接口 display interface display ip interface 6.查看保存的当前配置 display sav…

openinstall鸿蒙SDK再升级,功能全面支持HarmonyOS NEXT

万众期待的鸿蒙操作系统HarmonyOS NEXT即将发布&#xff0c;国产自主的全场景智能操作系统诞生&#xff0c;将为生态伙伴共创共享创造新蓝海&#xff0c;鸿蒙生态的加速构建&#xff0c;也有望催生出互联网生态的第三极。 作为首批鸿蒙生态伙伴&#xff0c;openinstall在App渠…

AR 眼镜之-蓝牙电话-来电铃声与系统音效

目录 &#x1f4c2; 前言 AR 眼镜系统版本 蓝牙电话 来电铃声 系统音效 1. &#x1f531; Android9 原生的来电铃声&#xff0c;走的哪个通道&#xff1f; 2. &#x1f4a0; Android9 原生的来电铃声&#xff0c;使用什么播放&#xff1f; 2.1 来电铃声创建准备 2.2 来…

C语言指针系列2——加深理解

大家&#xff0c;今天讲的知识点是一块儿一块儿的&#xff0c;所以大家要好好学习喔~ 话不多说&#xff0c;开始正题 关键字&#xff1a;const &#xff08;三种用法&#xff09; 1. int* const p; 2. int const* p; 3. const int* const p; 首先我们要了解一下const&#xff…

828华为云征文|使用Flexus X实例安装宝塔面板教学

目录 一、Flexus X实例简介 1.1 概述 1.2 产品规格 二、切换操作系统 2.1 Huawei Cloud EulerOS 2.0 标准版 2.2 切换镜像 三、部署宝塔面板 3.1 安装宝塔面板 3.2 放通安全组规则 3.3 登录宝塔面板 四、使用感受 4.1 柔性算力随心配 4.2 一直加速一直快 4.3 越用…