QT多线程编程(基础概念以及示例)

QT多线程编程

  • 前言:
  • 基础夯实:
    • 一:多线程概述
    • 二:QT多线程的使用
      • 1. 继承QThread类
      • 2. 继承QObject类
      • 3. QtConcurrent模块
    • 三:线程同步与通信
    • 四:线程安全
    • 五:线程管理
    • 六:总结
  • 效果展示:
  • 实现功能:
  • 核心代码:
    • mainwindow.h
    • mythread.h
    • mainwindow.cpp
    • main.cpp
    • mythread.cpp
  • 代码心得:
  • 仓库源码:

前言:

正好在做QT项目,感觉多线程编程很不错,虽然现在还没有用到,但是记录一下,和大家分享一下心得。

基础夯实:

一:多线程概述

多线程是指一个进程中包含至少两个执行流,即多个线程。每个线程都可以独立运行,访问该进程中的共享资源,并且可以与其它线程同步行动。多线程应用程序通常比单线程应用程序具有更好的响应速度和更好的资源利用率,适合于一些需要高效处理大量数据和执行复杂任务的场景。

二:QT多线程的使用

在QT中,使用QThread类可以方便地创建新的线程并在其中执行任务。以下介绍一些常用的QT多线程的技术和方法。

1. 继承QThread类

这是实现QT多线程的一种基本方式。主要步骤如下:

创建一个线程类的子类,继承Qt中的线程类QThread。
重写父类的run()方法,在函数内部编写子线程要处理的具体业务流程。run()方法是线程的入口点,类似于主线程中的main()函数。
在主线程中创建子类对象。
调用start()方法启动子线程。注意,不要直接调用run()方法,因为这会直接在主线程中执行,而不是在新的线程中。

2. 继承QObject类

另一种实现多线程的方式是继承QObject类,并通过moveToThread()方法将QObject对象移动到新创建的QThread中执行。主要步骤如下:

创建一个新的类,继承自QObject类。
在该类中添加公共的成员函数,函数体就是要在子线程中执行的业务逻辑。
在主线程中创建QThread对象和工作类对象。
将工作类对象移动到QThread对象中。
调用QThread对象的start()方法启动线程。
使用信号槽机制控制工作类对象的工作函数执行。

3. QtConcurrent模块

Qt还提供了QtConcurrent模块,这是一个在应用程序中创建并运行多个任务的高级方法。通过QtConcurrent::run()函数,可以方便地在后台线程中运行函数或Lambda表达式,而无需手动管理线程的生命周期。这种方式简化了多线程编程的复杂性,使得开发者可以更加专注于任务逻辑本身。

三:线程同步与通信

在多线程编程中,线程之间的同步和通信是非常重要的。QT提供了多种机制来实现这一点,包括互斥锁(QMutex)、读写锁(QReadWriteLock)、条件变量(QWaitCondition)以及信号槽机制等。

互斥锁(QMutex):用于保护共享资源,确保同一时间只有一个线程可以访问该资源。
读写锁(QReadWriteLock):允许多个线程同时读取共享资源,但写入时需要独占访问。
条件变量(QWaitCondition):用于线程之间的通信和同步,一个线程可以在某个条件不满足时等待,另一个线程在条件满足时通知等待的线程。
信号槽机制:Qt特有的跨线程通信机制,允许线程之间安全地发送信号和接收槽函数。

四:线程安全

在QT中,所有对UI的操作都必须放在主线程(GUI线程)中执行,因为QT的组件类和相关类只能工作在GUI线程。对于需要在工作线程中处理的数据或对象,需要确保线程安全,避免数据竞争和不一致的问题。

五:线程管理

在QT中,可以使用QThread类的各种函数来管理线程的生命周期,如start()、terminate()、wait()等。但需要注意的是,terminate()函数是强制终止线程的方法,可能会导致未定义的行为和数据损失,因此在实际开发中一般不建议使用。更推荐使用标志位和条件变量等机制来安全地终止线程。

六:总结

QT多线程编程是一个复杂但强大的功能,通过合理使用QThread类、QObject类以及QtConcurrent模块等工具和机制,可以实现高效、安全的多线程应用程序。开发者需要掌握线程的基本概念、QT多线程的使用方法、线程同步与通信机制以及线程安全和线程管理等方面的知识,才能充分发挥QT多线程编程的优势。

效果展示:

在这里插入图片描述

实现功能:

点击开始按钮,实现快速,冒泡,选择排序同时进行。

核心代码:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

signals:
    void starting(int num);

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QVector>
#include <QtGlobal>
#include <QDebug>
class Generate : public QThread
{
    Q_OBJECT
public:
    explicit Generate(QThread *parent = nullptr);

    void recvNum(int num);

protected:
    void run() override;

signals:
    void sendArray(QVector<int> num);

private:
    int m_num;

};

class BubbleSort : public QThread
{
    Q_OBJECT
public:
    explicit BubbleSort(QThread *parent = nullptr);

    void recvArray(QVector<int> list);

protected:
    void run() override;

signals:
    void Finish_Array(QVector<int> num);

private:
    QVector<int> m_list;

};
class SelectSort : public QThread
{
    Q_OBJECT
public:
    explicit SelectSort(QThread *parent = nullptr);

    void recvArray(QVector<int> list);

protected:
    void run() override;

signals:
    void Select_Array(QVector<int> num);

private:
    QVector<int> m_list;

};


class QuickSort : public QThread
{
    Q_OBJECT
public:
    explicit QuickSort(QThread *parent = nullptr);

    void recvArray(QVector<int> list);

protected:
    void run() override;
private:
    void quickSort(QVector<int> &list,int l ,int r);  //对list里起始位置l,结束位置r,进行排序

signals:
    void Finish_Array(QVector<int> num);

private:
    QVector<int> m_list;

};


#endif // MYTHREAD_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mythread.h"
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //创建子线程对象
    Generate* generate = new Generate;
    BubbleSort* bubble_sort = new BubbleSort;
    QuickSort* quick_sort = new QuickSort;
    SelectSort* select_sort = new SelectSort;
    //启动子线程
    connect(this , &MainWindow::starting, generate , &Generate::recvNum);
    connect(ui->start, &QPushButton::clicked , this ,[=](){
        emit starting(10000);
        generate->start();
    });
    connect(generate,&Generate::sendArray,bubble_sort,&BubbleSort::recvArray);
    connect(generate,&Generate::sendArray,quick_sort,&QuickSort::recvArray);
    connect(generate,&Generate::sendArray,select_sort,&SelectSort::recvArray);
    //接受子线程发送的数据
    connect(generate , &Generate::sendArray , this , [=](QVector<int> list){
       bubble_sort->start();
       quick_sort->start();
       select_sort->start();
       for (int i=0;i<list.size();++i){
           ui->rand_list->addItem(QString::number(list.at(i)));
       }
    });
    connect(bubble_sort , &BubbleSort::Finish_Array , this , [=](QVector<int> list){
       for (int i=0;i<list.size();++i){
           ui->bubbel_list->addItem(QString::number(list.at(i)));
       }
    });
    connect(quick_sort , &QuickSort::Finish_Array , this , [=](QVector<int> list){
       for (int i=0;i<list.size();++i){
           ui->quick_list->addItem(QString::number(list.at(i)));
       }
    });
    connect(select_sort , &SelectSort::Select_Array , this , [=](QVector<int> list){
        for (int i=0;i<list.size();++i){
            ui->select_list->addItem(QString::number(list.at(i)));
        }
    });

}

MainWindow::~MainWindow()
{
    delete ui;
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mythread.cpp

#include "mythread.h"
#include <QElapsedTimer>  //计算执行时间的类
#include <QtGlobal>
#include <QMutex>

static QMutex g_mutex;  // 线程锁
Generate::Generate(QThread *parent)
    : QThread{parent}
{

}

void Generate::recvNum(int num)
{
    m_num = num;
}

void Generate::run()
{
    qDebug() <<"生成随机数的线程的地址:"<<QThread::currentThread();  //输出当前线程的地址
    QVector<int> list;
    QElapsedTimer time;
    time.start();
    for (int i=0;i<m_num;i++){
        list.push_back(rand() % 100000);
    }
    int milsec = time.elapsed();

    qDebug() <<"生成"<<m_num<<"个随机数总共用时"<<milsec<<"毫秒";
    emit sendArray(list);
}
//------------------------------------------------------------冒泡排序
BubbleSort::BubbleSort(QThread *parent) :QThread(parent)
{

}

void BubbleSort::recvArray(QVector<int> list)
{
    m_list = list;
}

void BubbleSort::run()
{
    g_mutex.lock();
    qDebug() <<"冒泡排序的线程的地址:"<<QThread::currentThread();  //输出当前线程的地址
    QElapsedTimer time;
    time.start();
    int temp;
    for (int i=0;i<m_list.size();++i){
        for(int j = 0;j<m_list.size()-i-1;++j){
            if(m_list[j]>m_list[j+1]){
                temp = m_list[j];
                m_list[j] = m_list[j+1];
                m_list[j+1] = temp;
            }
        }
    }
    int milsec = time.elapsed();

    qDebug() <<"冒泡排序总共用时"<<milsec<<"毫秒";
    emit Finish_Array(m_list);
    g_mutex.unlock();
}

//------------------------------------------------------------选择排序
SelectSort::SelectSort(QThread *parent) :QThread(parent)
{

}

void SelectSort::recvArray(QVector<int> list)
{
    m_list = list;
}

void SelectSort::run()
{
    g_mutex.lock();
    qDebug() <<"选择排序的线程的地址:"<<QThread::currentThread();  //输出当前线程的地址
    QElapsedTimer time;
    time.start();
    int temp;
    for (int i = 0; i < m_list.size() - 1; ++i) {
        // 假设当前元素为最小值
        int minIndex = i;
        // 从当前元素的下一个位置开始寻找更小的元素
        for (int j = i + 1; j < m_list.size(); ++j) {
            if (m_list[j] < m_list[minIndex]) {
                minIndex = j;
            }
        }
        // 如果找到更小的元素,则交换它们
        if (minIndex != i) {
            std::swap(m_list[i], m_list[minIndex]);
        }
    }
    int milsec = time.elapsed();
    qDebug() <<"选择排序总共用时"<<milsec<<"毫秒";
    emit Select_Array(m_list);
    g_mutex.unlock();
}
//------------------------------------------------------------快速排序
void QuickSort::quickSort(QVector<int> &s, int l, int r) {
    if (l >= r) return;  // 递归结束条件
    int pivot = s[l];  // 基准元素
    int i = l, j = r;
    while (i < j) {
        while (i < j && s[j] >= pivot) j--;
        if (i < j) s[i++] = s[j];
        while (i < j && s[i] <= pivot) i++;
        if (i < j) s[j--] = s[i];
    }
    s[i] = pivot;
    quickSort(s, l, i - 1);  // 对左边子序列进行快速排序
    quickSort(s, i + 1, r);  // 对右边子序列进行快速排序
}

QuickSort::QuickSort(QThread *parent):QThread(parent)
{

}

void QuickSort::recvArray(QVector<int> list)
{
    m_list = list;
}

void QuickSort::run()
{
    qDebug() <<"快速排序的线程的地址:"<<QThread::currentThread();  //输出当前线程的地址
    QElapsedTimer time;
    time.start();

    quickSort(m_list,0,m_list.size()-1);

    int milsec = time.elapsed();

    qDebug() <<"快速排序总共用时"<<milsec<<"毫秒";
    emit Finish_Array(m_list);
}

代码心得:

代码可以简单分为四个模块,第一个模块产生随机数填充在列表项。其他三个模块分别是快速排序,冒泡排序,选择排序的算法,并将其结果显示在折叠列表项里面。四个类大同小异,这四个类包含的主要函数有三个,接受数据,处理数据,发送数据。在窗口函数中,实现四个线程的创建,然后启动,进行处理。

仓库源码:

需要看ui文件的,点击查看源码:gitee仓库源码

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络穿透:TCP 打洞、UDP 打洞与 UPnP

在现代网络中&#xff0c;很多设备都处于 NAT&#xff08;网络地址转换&#xff09;或防火墙后面&#xff0c;这使得直接访问这些设备变得困难。在这种情况下&#xff0c;网络穿透技术就显得非常重要。本文将介绍三种常用的网络穿透技术&#xff1a;TCP 打洞、UDP 打洞和 UPnP。…

SQL Server数据库简单的事务日志备份恢复

模拟数据库备份恢复过程 1.基础操作 1.创建TestDB数据库&#xff0c;并添加数据 USE [master] GO CREATE DATABASE TestDB CONTAINMENT NONE ON PRIMARY ( NAME NTestDB, FILENAME ND:\TestDB.mdf , SIZE 8192KB , MAXSIZE UNLIMITED, FILEGROWTH 65536KB ) LOG ON ( …

什么是API网关(API Gateway)?

1. 什么是API网关&#xff08;API Gateway&#xff09;&#xff1f; 在微服务体系结构中&#xff0c;客户端可能与多个前端服务进行交互。 API 网关位于客户端与服务之间。 它充当反向代理&#xff0c;将来自客户端的请求路由到服务。 它还可以执行各种横切任务&#xff0c;例…

GHOST重装后DEF盘丢失:深度解析与高效数据恢复方案

在数字信息爆炸的时代&#xff0c;数据安全与恢复成为了每个计算机用户必须面对的重要课题。GHOST作为系统备份与恢复领域的佼佼者&#xff0c;以其快速、便捷的特点赢得了广泛的用户基础。然而&#xff0c;在使用GHOST进行系统重装的过程中&#xff0c;不少用户遭遇了DEF盘&am…

【信创】Linux上图形化多ping工具--gping的编译安装与打包 _ 统信 _ 麒麟 _ 方德

原文链接&#xff1a;【信创】图形化多ping工具gping的编译安装与打包 | 统信 | 麒麟 | 方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在Linux操作系统上使用gping的文章。gping是一款非常实用的命令行工具&#xff0c;它将传统的ping命令进行了可视化改进…

【大数据方案】智慧大数据平台总体建设方案书(word原件)

第1章 总体说明 1.1 建设背景 1.2 建设目标 1.3 项目建设主要内容 1.4 设计原则 第2章 对项目的理解 2.1 现状分析 2.2 业务需求分析 2.3 功能需求分析 第3章 大数据平台建设方案 3.1 大数据平台总体设计 3.2 大数据平台功能设计 3.3 平台应用 第4章 政策标准保障体系 4.1 政策…

Java设计模式—面向对象设计原则(五) ----->迪米特法则(DP) (完整详解,附有代码+案例)

文章目录 3.5 迪米特法则(DP)3.5.1 概述3.5.2 案例 3.5 迪米特法则(DP) 迪米特法则&#xff1a;Demeter Principle&#xff0c;简称DP 3.5.1 概述 只和你的直接朋友交谈&#xff0c;不跟“陌生人”说话&#xff08;Talk only to your immediate friends and not to stranger…

【开放词汇检测】MM-Grounding-DINO论文翻译

摘要 Grounding-DINO 是一种先进的开放式检测模型&#xff0c;能够处理包括开放词汇检测&#xff08;Open-Vocabulary Detection&#xff0c;OVD&#xff09;、短语定位&#xff08;Phrase Grounding&#xff0c;PG&#xff09;和指代表达理解&#xff08;Referring Expressio…

人脸防伪检测系统源码分享

人脸防伪检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

(c++)用取模符%、除运算判断是否是某个数的倍数,判断个位十位是否有某个数

结果易懂&#xff0c;但凡和7有关&#xff0c;都输出相应的关系。关系分为三种&#xff0c;7的倍数&#xff0c;十位是7&#xff0c;个位是7 #include<iostream>using namespace std;int main() {for (int num 1; num < 100; num){if (num % 7 0)//是7的倍数{cout …

Android 13 固定systemUI的状态栏为黑底白字,不能被系统应用或者三方应用修改

目录 一.背景 二.思路 三.代码流程 1.colos.xml自定义颜色 2.设置状态栏的背景颜色 3.对View进行操作 ①.对Clock(状态栏左侧的数字时钟)进行操作 ②.对电池(BatteryMeterView)进行操作 4.锁屏状态栏 5.patch汇总 一.背景 客户需求将状态栏固定成黑底白字,并且不能让系…

Unity教程(十五)敌人战斗状态的实现

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

Pytest配置文件pytest.ini如何编写生成日志文件?

1、新建pytest.ini文件 [pytest] log_clitrue log_leveLNOTSET log_format %(asctime)s %(levelname)s %(message)s %(filename)s %(funcName)s %(lineno)d log_date_format %Y-%m-%d %H:%M:%Slog_file ./logdata/log.log log_file_level info log_file_format %(asctime…

【6大设计原则】迪米特法则:解密软件设计中的“最少知识原则”

引言 在软件设计中&#xff0c;设计原则是指导我们构建高质量、可维护系统的基石。迪米特法则&#xff08;Law of Demeter&#xff0c;LoD&#xff09;&#xff0c;也被称为“最少知识原则”&#xff0c;是六大设计原则之一。它强调对象之间的松耦合&#xff0c;确保系统的各个…

爬虫代理API的全面解析:让数据抓取更高效

在大数据时代&#xff0c;网络爬虫已经成为收集和分析数据的重要工具。然而&#xff0c;频繁的请求会导致IP被封禁&#xff0c;这时候爬虫代理API就显得尤为重要。本文将详细介绍爬虫代理API的作用、优势及如何使用&#xff0c;帮助你更高效地进行数据抓取。 什么是爬虫代理AP…

注解(Java程序的一种特殊“注释”,用于工具处理的标注)

1.使用注解 a.含义 i.注解是放在类&#xff0c;字段&#xff0c;方法&#xff0c;参数前的一种特殊“注释”。 ii.注释会被编译器直接忽略&#xff0c;注解则可以被编译器打包带进class文件&#xff0c;因此&#xff0c;注解是一种用于标注的“元数据”。 iii…

设计模式之建造者模式(通俗易懂--代码辅助理解【Java版】)

文章目录 设计模式概述1、建造者模式2、建造者模式使用场景3、优点4、缺点5、主要角色6、代码示例&#xff1a;1&#xff09;实现要求2&#xff09;UML图3)实现步骤&#xff1a;1&#xff09;创建一个表示食物条目和食物包装的接口2&#xff09;创建实现Packing接口的实体类3&a…

NeMo Curator 整理用于 LLM 参数高效微调的自定义数据集

目录 概述 预备知识 定义自定义文档构建器 下载数据集 解析和迭代数据集 将数据集写入 JSONL 格式 使用文档构建器加载数据集 使用现有工具统一 Unicode 格式 设计自定义数据集过滤器 编辑所有个人识别信息 添加指令提示 整合管线 概述 出于演示目的&#xff0c;本…

QXml 使用方法

VS2019 QT 编译工具链问题解决 使用winqtdeploy.exe 打包环境就可以正常运行&#xff0c;缺少某一个运行库引起的 简易使用python脚本编译运行 Python3 中的 slots 和 QT 中的 slots 宏定义重复, 放在不同的文件中进行调用可以避免 还是比较习惯从源码包引入&#xff08;方便定…

相机光学(三十七)——自动对焦原理

1.自动对焦的三种方式 目前在手机上采用的自动对焦系统包括反差对焦、相位对焦和激光对焦三种方案&#xff0c;下面我们来看一下它们的工作原理和相互之间的区别是什么。 1.1反差对焦【CDAF】- Contrast Detection Auto Focus 反差对焦是目前普及率最高、使用最广泛、成本相对…