当前位置: 首页 > news >正文

七、Qt框架编写的多线程应用程序

一、大纲

学习内容:使用两个线程,分别点击两个按钮,触发两个不同的效果
所需控件:两个button、三个label
涉及知识点:多线程、Qt的connect机制、定时器、互斥锁
需求:
1,多线程定时计数:创建两个独立的线程,每个线程中运行一个定时器,定时器分别以不同的时间间隔触发,每次触发时更新界面上的标签,显示该线程定时器的调用次数。
2,按钮点击计数:界面上有两个按钮,每个按钮被点击时,更新相应的标签,显示该按钮的点击次数。

在工作中涉及到采用多线程读取PLC同一块内存,将核心思路进行了总结,并抽离了一下,用比较简单的demo进行演示,故此作为笔记,后续有利于复习

二、应用场景

1,工业自动化领域
通过多线程读取PLC的同一个DB块的内存数据
2,游戏开发领域
多线程定时计数功能可以实现游戏中的倒计时功能,比如限时任务的剩余时间、技能冷却时间等。
3,智能家居领域
多线程定时计数功能可以定时收集设备的能耗数据,通过统计一段时间内的能耗变化,分析设备的使用习惯和能耗规律。

三、创建Qt项目

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
目录结构
在这里插入图片描述

四、UI布局

在wdiget里面有俩button和三个label
在这里插入图片描述

五、代码

1,main.cpp

#include "qt_thread.h"
#include <QtWidgets/QApplication>
#include <QTextCodec>


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    beyondyanyu_study_qt_thread::QtThread w;
    w.show();
    return a.exec();
}

2,qt_thread.h

#pragma once

#include <QtWidgets/QMainWindow>
#include <QMutex>
#include "ui_QtThread.h"

namespace beyondyanyu_study_qt_thread {
    class QtThread : public QMainWindow
    {
        Q_OBJECT

    public:
        QtThread(QWidget* parent = nullptr);
        ~QtThread();

    private slots:
        void PushButton1_clicked();
        void PushButton2_clicked();
       
    private:
        Ui::QtThreadClass ui;

        QThread* thread_1_;
        QThread* thread_2_;

        QTimer* timer_label_1_;
        QTimer* timer_label_2_;

        int counter_1_{0};
        int counter_2_{0};
        int counter_3_{0};
        int counter_4_{0};

        QMutex mutex_{ QMutex::Recursive };

    };
};// namespace beyondyanyu_study_qt_thread

成员变量

Ui::QtThreadClass ui;:这是用于存储 UI 界面元素的对象,一般由 Qt 的 UI 设计器生成。借助 ui 能访问并操作界面上的各类控件,例如按钮、标签等。
QThread* thread_1_; QThread* thread_2_;:这两个指针分别指向两个不同的 QThread 对象,用来创建和管理两个独立的线程。每个线程都可并行执行任务,互不干扰。
QTimer* timer_label_1_; QTimer* timer_label_2_;:这两个指针分别指向两个 QTimer 对象,用于定时触发特定操作。timer_label_1_ timer_label_2_能以不同的时间间隔触发超时信号。
int counter_1_{0};int counter_2_{0};int counter_3_{0};int counter_4_{0};:这些整型变量作为计数器使用。counter_1_ 和 counter_2_ 分别记录 timer_label_1_ 和 timer_label_2_ 定时器的超时次数;counter_3_ 和 counter_4_ 分别记录 ui.pushButton 和 ui.pushButton_2 按钮的点击次数。
QMutex mutex_{ QMutex::Recursive };:这是一个递归互斥锁对象,用于保证多线程环境下对共享资源(如 counter_1_ 和 counter_2_)的安全访问,防止数据竞争问题。

成员函数

QtThread(QWidget* parent = nullptr);:这是 QtThread 类的构造函数,用于初始化 QtThread 对象。在构造函数里会进行界面的初始化、线程的启动、定时器的设置以及信号与槽的连接等操作。
~QtThread();:这是 QtThread 类的析构函数,负责在对象销毁时释放相关资源,不过当前代码里该析构函数为空。
void PushButton1_clicked(); void PushButton2_clicked();:这两个函数属于槽函数,会在 ui.pushButton 和 ui.pushButton_2 按钮被点击时分别触发,用于更新相应的计数器并修改界面上的标签显示。

3,qt_thread.cpp

#include "qt_thread.h"
#include <QThread>
#include <QTimer>



namespace beyondyanyu_study_qt_thread {
    QtThread::QtThread(QWidget* parent)
        : QMainWindow(parent)
    {
        ui.setupUi(this);

        //按钮一
        //开启线程
        thread_1_ = new QThread();
        thread_1_->start();

        //开启一个定时器
        timer_label_1_ = new QTimer(this);
        timer_label_1_->setInterval(1000);
        timer_label_1_->start();

        //将改定时器移动到线程中
        timer_label_1_->moveToThread(thread_1_);

        connect(timer_label_1_, &QTimer::timeout, timer_label_1_, [this] 
        {
             QMutexLocker locker(&mutex_);
             counter_1_++;
             ui.label_3->setText(QStringLiteral("线程1调用次数:") + QString::number(counter_1_));
        });



        //按钮二
        //开启线程
        thread_2_ = new QThread();
        thread_2_->start();

        //开启一个定时器
        timer_label_2_ = new QTimer(this);
        timer_label_2_->setInterval(900);
        timer_label_2_->start();

        //将改定时器移动到线程中
        timer_label_2_->moveToThread(thread_2_);

        connect(timer_label_2_, &QTimer::timeout, timer_label_2_, [this]
        {
            QMutexLocker locker(&mutex_);
            counter_2_++;
            ui.label_3->setText(QStringLiteral("线程2调用次数:") + QString::number(counter_2_));
        });

        connect(ui.pushButton, &QPushButton::clicked, this, &QtThread::PushButton1_clicked);
        connect(ui.pushButton_2, &QPushButton::clicked, this, &QtThread::PushButton2_clicked);

    }

    void QtThread::PushButton1_clicked()
    {

        counter_3_++;
        ui.label->setText(QStringLiteral("按钮1触发次数:") + QString::number(counter_3_));
    
    }
    void QtThread::PushButton2_clicked()
    {
        counter_4_++;
        ui.label_2->setText(QStringLiteral("按钮2触发次数:") + QString::number(counter_4_));
    }

    QtThread::~QtThread()
    {
        // 停止线程
        thread_1_->quit();
        thread_1_->wait();
        delete thread_1_;

        thread_2_->quit();
        thread_2_->wait();
        delete thread_2_;

        // 删除定时器
        delete timer_label_1_;
        delete timer_label_2_;
    }
} // namespace beyondyanyu_study_qt_thread;

构造函数 QtThread::QtThread(QWidget* parent)

ui.setupUi(this);:初始化 UI 界面,把 UI 设计器设计的界面元素加载到 QtThread 窗口中。
按钮一相关操作:
thread_1_ = new QThread(); thread_1_->start();:创建并启动一个新线程 thread_1_。
timer_label_1_ = new QTimer(this);timer_label_1_->setInterval(1000); timer_label_1_->start();:创建一个定时器 timer_label_1_,设置其时间间隔为 1000 毫秒(即 1 秒),然后启动该定时器。
timer_label_1_->moveToThread(thread_1_);:将 timer_label_1_ 定时器移动到 thread_1_ 线程中运行。
connect(timer_label_1_, &QTimer::timeout, timer_label_1_, [this] {... });:把 timer_label_1_ 的 timeout 信号连接到一个 Lambda 表达式。当定时器超时时,会执行 Lambda 表达式中的代码,使用互斥锁保护 counter_1_ 计数器的更新操作,并更新界面上 ui.label_3 标签的显示内容。
按钮二相关操作:操作与按钮一类似,不同之处在于 timer_label_2_ 的时间间隔设置为 900 毫秒。
connect(ui.pushButton, &QPushButton::clicked, this, &QtThread::PushButton1_clicked); connect(ui.pushButton_2, &QPushButton::clicked, this, &QtThread::PushButton2_clicked);:将 ui.pushButton 和 ui.pushButton_2 按钮的 clicked 信号分别连接到 PushButton1_clicked() 和 PushButton2_clicked() 槽函数。

槽函数 void QtThread::PushButton1_clicked() 和 void QtThread::PushButton2_clicked()

PushButton1_clicked():当 ui.pushButton 按钮被点击时,counter_3_ 计数器加 1,同时更新界面上 ui.label 标签的显示内容,显示按钮 1 的点击次数。
PushButton2_clicked():当 ui.pushButton_2 按钮被点击时,counter_4_ 计数器加 1,同时更新界面上 ui.label_2 标签的显示内容,显示按钮 2 的点击次数。

析构函数 QtThread::~QtThread()

当前析构函数为空,在实际应用中,需要释放 thread_1_、thread_2_、timer_label_1_ 和 timer_label_2_ 等动态分配的资源,避免内存泄漏。

四、效果演示

在这里插入图片描述
在这里插入图片描述

相关文章:

  • JDK的卸载与安装
  • 【JavaScript——页面渲染】课程列表(蓝桥杯真题-2457)【合集】
  • 《从零搭建Vue3项目实战》(AI辅助搭建Vue3+ElemntPlus后台管理项目)零基础入门系列第十二篇(完结篇):数据统计功能实现
  • Java高性能并发利器-VarHandle
  • 【sgSpliter】自定义组件:可调整宽度、高度、折叠的分割线
  • 【技术派部署篇】云服务器部署技术派
  • jeecg启动所需要安装的软件
  • GitHub Desktop 推送报错 Authentication Failed 身份验证失败
  • HarmonyOS 5.0分布式开发深度踩坑指南:从理论到实践的突围之路
  • Java递归练习----猴子偷桃
  • 基于ueditor编辑器的功能开发之增加自定义一键排版功能
  • Java IO 流
  • 【资料分享】瑞芯微RK3576,8核2.2GHz+6T算力NPU工业核心板说明书
  • STM32(基于标准库)
  • 多模态大模型[CLIP/Flamingo/Coca/BLIP]
  • Unity入门
  • 图谱可视化的海洋生物信息查询网站的设计与实现(springboot+ssm+vue)含文档
  • 十八、TCP多线程、多进程并发服务器
  • 气动V型调节开关球阀气源连接尺寸与方式全解析-耀圣
  • 2025 GGS全球游戏峰会前瞻预告:全新版本控制平台Perforce P4、龙智游戏开发及管理解决方案等即将亮相
  • 温州专业营销网站制作/武汉网站设计
  • 哪些网站可以免费做推广呢/域名注册网站查询
  • 北京网站建设公司动感/网站seo服务
  • 烟台免费网站建设/搜索引擎优化排名培训
  • 顺德网站制作公司/软文拟发布的平台与板块
  • 设置网络的网站/国内真正的永久免费建站