【嵌入式Qt开发入门】Qt如何使用多线程——初识QThread

news2024/9/28 1:08:21

        我们写的一个应用程序,应用程序跑起来后一般情况下只有一个线程,但是可能也有特殊情况。比如我们前几篇的例程都跑起来后只有一个线程,就是程序的主线程。线程内的操作都是顺序执行的。但试着想一下,我们的程序顺序执行,假设我们的用户界面点击有某个操作是比较耗时的。您会发现界面点击完了,点击界面对应的操作还没有完成,所以就会冻结界面,不能响应,直到操作完成后,才返回到正常的界面里,对用户的体验也会造成极其糟糕的影响。

        这种情况我们一般是创建一个单独的线程来执行这个比较耗时的操作。比如我们使用摄像头拍照保存照片。也许你会觉得在电脑上使用 Qt 拍照,处理起来非常快,根本也不需要开启一个线程来做这种事。但是,我们是否考虑在嵌入式的 CPU 上做这种事情呢?嵌入式的 CPU 大多数都没有电脑里的 CPU 主频(几 GHz)那么高,处理速度也不快。 此时我们就需要考虑开多一个线程来拍照了。拍完照再与主线程(即程序原来的线程)处理好照片的数据,就完成了一个多线程的应用程序了。

        官方文档里说,QThread 类提供了一种独立于平台的方法来管理线程。QThread 对象在程序中管理一个控制线程。QThreads 在 run()中开始执行。默认情况下,run()通过调用 exec()来启动事件循环,并在线程中运行 Qt 事件循环。您可以通过使用 QObject::moveToThread()将 worker 对象移动到线程来使用它们。

        QThread 线程类是实现多线程的核心类。Qt 有两种多线程的方法,其中一种是继承 QThread 的 run()函数,另外一种是把一个继承于 QObject 的类转移到一个 Thread 里。具体之区别读者们可以自己查阅。

        接下来用一两篇的篇幅来介绍如何使用 QThread 实现多线程编程,讲解如何通过继承 QThread 和 QObject 的方法来创建线程。还会使用 QMutexLocker 正确的退出一个线程。主要以应用为主,并不深入,所以不难,目的就是快速掌握 Qt 线程的创建,理解线程。

        关于多线程的学习,希望可以结合Linux线程管理的知识Linux线程同步(1)——一个例子看懂为什么需要线程同步?来学习,对于已经学过的知识温故知新、常学常新。

继承 QThread 的线程

      首先,学习继承 QThread 是创建线程的一个普通方法。其中创建的线程只有 run()方法在线程里的。其他类内定义的方法都在主线程内。我们画个图来帮助理解。

         通过上面的图我们可以看到,主线程内有很多方法在主线程内,但是子线程,只有 run() 方法是在子线程里的。run()方法是继承于 QThread 类的方法,用户需要重写这个方法,一般是把耗时的操作写在这个run()方法里面。

应用实例

        项目名称:qthread_example1 ,继承 QThread 类的线程。

        通过 QThread 类继承线程,然后在 MainWindow 类里使用。通过点击一个按钮开启线程。当线程执行完成时,会发送 resultReady(const QString &s)信号给主线程。

        在头文件“mainwindow.h”具体代码如下。

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>
#include <QDebug>
#include <QPushButton>

/* 使用下面声明的WorkerThread线程类 */
class WorkerThread;

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    /* 在MainWindow类里声明对象 */
    WorkerThread *workerThread;

    /* 声明一个按钮,使用此按钮点击后开启线程 */
    QPushButton *pushButton;

private slots:
    /* 槽函数,用于接收线程发送的信号 */
    void handleResults(const QString &result);

    /* 点击按钮开启线程 */
    void pushButtonClicked();
};

/* 新建一个WorkerThread类继承于QThread */
class WorkerThread : public QThread
{
    /* 用到信号槽即需要此宏定义 */
    Q_OBJECT

public:
    WorkerThread(QWidget *parent = nullptr) {
        Q_UNUSED(parent);
    }

    /* 重写run方法,继承QThread的类,只有run方法是在新的线程里 */
    void run() override {
        QString result = "线程开启成功";

        /* 这里写上比较耗时的操作 */
        // ...
        // 延时2s,把延时2s当作耗时操作
        sleep(2);

        /* 发送结果准备好的信号 */
        emit resultReady(result);
    }

signals:
    /* 声明一个信号,译结果准确好的信号 */
    void resultReady(const QString &s);
};

#endif // MAINWINDOW_H

       

        此处声明一个 WorkerThread 的类继承 QThread 类,这里是参考 Qt 的 QThread 类的帮助文档的写法。

        重写 run()方法,这里很重要。把耗时操作写于此。

        在源文件“mainwindow.cpp”具体代码如下。

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 设置位置与大小 */
    this->setGeometry(0, 0, 800, 480);

    /* 对象实例化 */
    pushButton = new QPushButton(this);
    workerThread = new WorkerThread(this);

    /* 按钮设置大小与文本 */
    pushButton->resize(100, 40);
    pushButton->setText("开启线程");

    /* 信号槽连接 */
    connect(workerThread, SIGNAL(resultReady(QString)),
            this, SLOT(handleResults(QString)));
    connect(pushButton, SIGNAL(clicked()),
            this, SLOT(pushButtonClicked()));
}

MainWindow::~MainWindow()
{
    /* 进程退出,注意本例run()方法没写循环,此方法需要有循环才生效 */
    workerThread->quit();

    /* 阻塞等待2000ms检查一次进程是否已经退出 */
    if (workerThread->wait(50)) {
        qDebug()<<"线程已经结束!"<<endl;
    }
}

void MainWindow::handleResults(const QString &result)
{
    /* 打印出线程发送过来的结果 */
    qDebug()<<result<<endl;
}

void MainWindow::pushButtonClicked()
{
    /* 检查线程是否在运行,如果没有则开始运行 */
    if (!workerThread->isRunning())
        workerThread->start();
}

        首先,线程对象实例化,Qt 使用 C++基本都是对象编程,Qt 线程也不例外。所以我们也是用对象来管理线程的。

        然后,在 MainWindow 的析构函数里退出线程,然后判断线程是否退出成功。因为我们这个线程是没有循环操作的,直接点击按钮开启线程后,做了 2s 延时操作后就完成了。所以我们在析构函数里直接退出没有关系。

        最后,按钮点击后开启线程,首先我们得判断这个线程是否在运行,如果不在运行我们则开始线程,开始线程用 start()方法,它会调用重写的 run()函数的。

程序运行效果

        点击开启线程按钮后,延时 2s 后,Qt Creator 的应用程序输出窗口打印出“线程开启成功”。
在 2s 内多次点击按钮则不会重复开启线程,因为线程在这 2s 内还在运行。同时我们可以看到
点击按钮没卡顿现象。因为这个延时操作是在我们创建的线程里运行的,而 pushButton 是在主
线程里的,通过点击按钮控制子线程的运行。
        当关闭程序后,子线程将在主线程的析构函数里退出。注意线程使用 wait()方法,这里等
待 2s,因为我们开启的线和是延时 2s 就完成了。如果是实际的操作,请根据 CPU 的处理能力,
给一个适合的延时,阻塞等待线程完成后,就会自动退出并打印“线程已经结束”。

 

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

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

相关文章

模拟日志数据

模拟日志数据。。 日志模拟器主要模拟站点的用户信息和用户行为日志数据 使用shell调用java类实现数据的生产 ! 所需文件如下: datagen.jar genlog.sh 注意: 存储数据的文件夹要预先创建好 Bash mkdir -p /tmp/doit/user_data mkdir -p /tmp/doit/log_data 将用来模拟生成…

【安全】Xsslabs(1~13)基于白盒测试浅析

目录 环境 关卡 level 1 level 2 level 3 level 4 level 5 level 6 level 7 level 8 扩展 level 9 level 10 level 11 level 12 level 13 总结 环境 PHP&#xff1a;php7.3.4nts 中间件&#xff1a;Nginx1.15.11 工具&#xff1a;Hackbar 关卡 level …

超长上下文处理:基于Transformer上下文处理常见方法梳理

原文链接&#xff1a;芝士AI吃鱼 目前已经采用多种方法来增加Transformer的上下文长度&#xff0c;主要侧重于缓解注意力计算的二次复杂度。 例如&#xff0c;Transformer-XL通过缓存先前的上下文&#xff0c;并允许随着层数的增加线性扩展上下文。Longformer采用了一种注意力…

【GLBCXX_3.4.21 not found】问题解决

问题描述 rootlocalhost:/home# rar -h rar&#xff1a;/lib64/libstdc.so.6 ,version "GLBCXX_3.4.21" not found 问题排查过程 下载相应的GCC源码&#xff0c;找到适合自己版本&#xff0c;这里选择是的是7.3.0 gcc源码下载地址&#xff1a;http://ftp.gnu.org/g…

fastadmin数据内容admin_id权限

/*** 是否开启数据限制* 支持auth/personal* 表示按权限判断/仅限个人* 默认为禁用,若启用请务必保证表中存在admin_id字段*/protected $dataLimit true;/*** 数据限制字段*/protected $dataLimitField admin_id;

第一次上传GitHub代码------教程、记录和踩雷

刚录用的一篇论文&#xff0c;编辑要求上传GitHub repo。 对于上传代码&#xff0c;本人是一无所知&#xff0c;写这篇博客记录一下 一. 创建代码仓库 点击右上角&#xff0c;选择New repository&#xff0c;来创建仓库&#xff1a; 根据提示&#xff0c;填写仓库的名称、描述…

TCP 协议(二)连接与断开

TCP 连接概述 TCP 协议是一种面向连接的、可靠的数据传输协议&#xff0c;同时 TCP 连接是全双工的&#xff0c;即连接的两端可以互传数据。在深入了解 TCP 连接之前&#xff0c;我们先来弄清楚整个 TCP 连接的过程&#xff0c;之后在深入整个数据报文结构来认识 TCP。 TCP连…

全网最全,adb常用命令大全(详细)全覆盖,看这篇就够了..

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 adb 模拟按键输入…

python subprocess执行外部命令常用方法

subprocess模块是Python标准库中的一个模块&#xff0c;用于创建和控制子进程。它提供了一种在Python程序中调用其他外部命令、执行系统命令和与系统进程进行交互的方法。常用的有两种方法&#xff1a;subprocess.run()&#xff0c;subprocess.Popen() 1. subprocess.run()方法…

回顾类与对象:掌握String探索其模拟实现的沉浸式体验

目录 一.STL简介二.string的模拟实现1.成员变量与(拷贝)构造、析构函数2.运算符重载[ ]3.添加数据与扩容4.赋值运算符重载及其他重载5.其他函数 一.STL简介 标准模板库 STL是C标准库的重要组成部分&#xff0c;stl分为六大组件&#xff1a;算法、容器、迭代器、空间适配器、仿…

NLP实战7:seq2seq翻译实战-Pytorch复现

&#x1f368; 本文为[&#x1f517;365天深度学习训练营]内部限免文章&#xff08;版权归 *K同学啊* 所有&#xff09; &#x1f356; 作者&#xff1a;[K同学啊] &#x1f4cc; 本周任务&#xff1a; ●请根据N5、N6周内容&#xff0c;为解码器添加上注意力机制 一、前期准备…

常用分类损失CE Loss、Focal Loss及GHMC Loss理解与总结

一、CE Loss 定义 交叉熵损失&#xff08;Cross-Entropy Loss&#xff0c;CE Loss&#xff09;能够衡量同一个随机变量中的两个不同概率分布的差异程度&#xff0c;当两个概率分布越接近时&#xff0c;交叉熵损失越小&#xff0c;表示模型预测结果越准确。 公式 二分类 二…

【QT】QT搭建OpenCV环境

QT/OpenCV 01、开始之前02、QT03、CMake04、OpenCV05、配置06、测试 01、开始之前 本文版本&#xff1a; 1、QT&#xff1a;Based on Qt 5.12.2 (MSVC 2017, 32 bit)&#xff0c;编译方式是MinGW 2、CMake&#xff1a;cmake-3.27.0-rc4-windows-x86_64.msi 3、OpenCV&#xff1…

2023年值得入手的开放式耳机推荐,蓝牙耳机的选购指南分享推荐

身为一个音乐爱好者&#xff0c;出于对音质和佩戴舒适的追求&#xff0c;也有入手了很多品类的耳机&#xff0c;其中不乏有有线耳机、无线蓝牙耳机&#xff0c;两种不同的音频传输方式大类&#xff0c;其各自所拥有的特性也是不同的。而居于后者的无线蓝牙耳机&#xff0c;在现…

【Java基础教程】(八)面向对象篇 · 第二讲:Java 数组全面解析——动态与静态初始化、二维数组、方法参数传递、排序与转置、对象数组、操作API~

Java基础教程之面向对象 第二讲 本节学习目标1️⃣ 概念1.1 动态初始化1.2 静态初始化 2️⃣ 二维数组3️⃣ 数组与方法参数的传递4️⃣ 数组排序5️⃣ 数组转置6️⃣ 对象数组7️⃣ 数组操作API7.1 数组复制7.2 数组排序 &#x1f33e; 总结 本节学习目标 掌握数组的动态及静…

水库监测中仪器安装及监测结果的要求有哪些

水库监测点位布设需要根据水库运行情况和安全监测的需求来进行&#xff0c;一般分为基础监测点位和重要部位监测点位&#xff0c;基础监测点位主要包括上游水位、上游库水位变幅、库岸稳定以及上下游坝坡稳定等。重要部位监测点位主要包括坝轴线、溢洪道进口和泄水洞出口等部位…

前端报错:“Uncaught SyntaxError: missing ) after argument list“只是参数列表后面缺少 “)”?

报错"Uncaught SyntaxError: missing ) after argument list"&#xff0c;字面翻译过来的意思&#xff1a;语法错误: 参数列表后面缺少 )。 一直以为是少了 一个小括号找了好久 发现并不是 据提示是参数列表的问题&#xff0c;找到文件中存在参数列表的地方。如下图…

如何利用MyBatis完成web项目的环境搭建(导入核心依赖包、日志、编译环境,配置文件以及Druid连接池)

目录 项目环境搭建 servlet实例 核心依赖 导入日志 编译环境 mapper注册 resouces中 dao中 MyBatis配置文件 实例效果 导入配置文件 Druid连接池 Druid连接池是什么&#xff1f; 如何配置Druid连接池&#xff1f; 实体类 实例效果 项目环境搭建 1.在pom.xml中…

STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059

STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059 Proteus仿真小实验&#xff1a; STM32 Proteus UCOSII系统锅炉报警系统设计压力温度水位-0059 功能&#xff1a; 硬件组成&#xff1a;51单片机 8位数码管MAX7219数码管驱动模块多个按键LED灯蜂鸣器 1.准确测量…

IronOCR for .NET 2023.7.0 Crack

IronOCR for .NET 关于 读取 .NET 应用程序中图像和 Pdf 文本的高级 OCR &#xff08;光学字符识别&#xff09; 库。 IronOCR for .NET enables software engineers to read text content from images & PDFs in .NET applications and Web sites. Read text and barcod…