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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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.rhkb.cn/news/423728.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s 部署 ruoyi 前后端分离项目

本文视频版: https://www.bilibili.com/video/BV17ugkePEeN 参考 https://blog.csdn.net/qq_50247813/article/details/136934090 https://gitee.com/nasaa/RuoYi-Vue-cloud https://www.itsgeekhead.com/tuts/kubernetes-129-ubuntu-22-04-3/ https://kubernetes.io/docs…

【信号】SIGCHLD信号--了解

SIGCHLD是多少号信号呢&#xff1f;17号 我们知道用wait和waitpid函数清理僵尸进程,父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)。采用第一种方式,父进程阻塞了就不能处理自己的工作了;采用第二种方式,父进程在处理自己的工作…

Leetcode 二叉树中根遍历

采用递归算法&#xff0c;并且用一个向量来存储节点值。 算法C代码如下&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}*…

Python库Plotly学习笔记

Plotly是一个用于创建交互式图形的Python库。它提供了丰富的图表类型&#xff0c;包括散点图、线图、柱状图、热力图、饼图等&#xff0c;以及高级的可视化功能&#xff0c;如动画、交互、数据缩放和拖动等。 Plotly库的主要特点&#xff1a; 交互式图表&#xff1a;Plotly创…

git 更新LingDongGui问题解决

今天重新更新灵动gui的代码&#xff0c;以便使用最新的arm-2d&#xff0c;本来以为是比较简单的一件事情&#xff08;因为以前已经更新过一次&#xff09;&#xff0c;却搞了大半天&#xff0c;折腾不易啊&#xff0c;简单记录下来&#xff0c;有同样遇到问题的同学参考&#x…

SSM框架学习(三、MyBatis实践:提高持久层数据处理效率)

目录 一、Mybatis简介 1.简介 2.持久层框架对比 3.快速入门&#xff08;基于Mybatis3方式&#xff09; 4.ibatis方式的实现和原理 5.ibatis与mybatis之间的关系 二、Mybatis基本使用 1.向 sql 语句传参 &#xff08;1&#xff09;mybatis日志输出配置 &#xff08;2&…

存储课程学习笔记5_iouring的练习(io_uring,rust_echo_bench,fio)

我们知道&#xff0c;在处理大量高并发网络时&#xff0c;一般考虑并发&#xff0c;以及设计对应的方案&#xff08;比如select,poll,epoll&#xff09;等。 那么如果频繁进行文件或者磁盘的操作&#xff0c;如何考虑性能和并发&#xff0c;这里就可以考虑用到io_uring。 0&a…

RK3588镜像打包制作,替换文件系统

1.在开发板上安装async apt-get async 2.在另一台linux机器上执行命令拷贝文件系统 注意&#xff1a; 这里使用root权限或者账户 mkdir rootfs rsync -avx root192.168.1.3:/ rootfs 3.制作空镜像文件 先去开发板上验证自己的系统使用了多少空间&#xff0c;然后输入命令制…

rancker 图形化界面

rancker 图形化界面 图形化界面进行k8s集群的管理 rancher自带监控————普罗米修斯 #在master和两个node上都操作 [rootmaster01 opt]# rz -E rz waiting to receive. [rootmaster01 opt]# docker load -i rancher.tar ​ #在master上操作 [rootmaster01 opt]# docker pul…

828华为云征文|华为云Flexus X搭建借贷管理系统、二次开发借贷小程序 前端源码uniapp

在华为云828 B2B企业节的盛宴中&#xff0c;Flexus X实例以其卓越的算力性能和灵活的资源配置脱颖而出。对于追求极致性能、渴望在借贷管理、电商交易等场景中脱颖而出的您来说&#xff0c;Flexus X无疑是最佳拍档。搭载创新加速引擎&#xff0c;让您的自建MySQL、Redis、Nginx…

浙大数据结构:04-树6 Complete Binary Search Tree

这道题利用了完全二叉树的性质&#xff0c;我也参考了一些代码写的。 &#xff08;自己一开始写了别的方法&#xff0c;但一直过不了最后一个测试点&#xff0c;红温了&#xff09; 机翻&#xff1a; 1、条件准备 用vector存输入的数据&#xff0c;另一个数组存输出的结果&a…

实战外网配置——光猫桥接+路由器PPPoE拨号+防火墙外网链路健康检查+外网流量负载均衡

一、适用场景&#xff1a; 1、企业规模较大时&#xff0c;1条公网带宽流量可能不足&#xff0c;需要用到多条公网出口时。 2、企业有业务需要静态ip映射&#xff0c;但是因静态ip专线价格较高&#xff0c;所以需要拨号光纤承载较多的下行流量。 3、当公网出口有多条链路&#…

学习笔记 - 知识图谱的符号表示方法

学习笔记 - 知识图谱的符号表示方法 说明&#xff1a; 首次发表日期&#xff1a;2024-09-13个人阅读学习并摘录成笔记 知识表示的相关名词定义 以下内容摘录自 Knowledge Graphs Applied 2.3小节&#xff0c;然后AI翻译人工润色。 实体&#xff08;Entities&#xff09;—表…

Python | 练习作业 2

为学生登录系统新增搜索功能。 第二天作业的解题思路&#xff1a; # 1.创建一个空列表保存搜索结果 # 2.让用户输入要搜索的内容 # 3.遍历学生信息&#xff0c;检查学生的id name age gender score # 中的属性值 是否跟用户搜索的内容一致 # 4.如果有一致的属性 那么就将该学生…

TikTok运营需要的独立IP如何获取?

TikTok作为当下炙手可热的社交媒体平台&#xff0c;吸引了众多个人创作者和企业进驻。在进行TikTok运营时&#xff0c;许多经验丰富的用户都倾向于选择独立IP。那么&#xff0c;TikTok运营为什么需要独立IP&#xff1f;又该如何获取呢&#xff1f;本文将详细为您解答这些问题。…

vue2基础系列教程之v-model及面试高频问题

v-model是表单组件里面的核心知识点&#xff0c;这个指令给我们写表单业务带来了很大的方便。 元素标签上的 v-model 指令用于双向绑定数据,它是一个语法糖&#xff0c;可以用于代替 v-bind:value 和 input 例如&#xff1a;<input v-model"message" placeholder…

Springboot中mybatis的使用

一.创建Springboot项目并加载依赖 1.利用IDEA创建SpringBoot项目&#xff0c;并勾选必须依赖&#xff0c;步骤如下&#xff08;IDEA版本为2024版&#xff09; 注意&#xff1a; 1.首先更换镜像源&#xff0c;否则加载配置环境比较慢&#xff0c;网上搜阿里的镜像源就行。 2…

Python数据类型详解:这12个类型你都知道吗?

在Python中&#xff0c;数据类型是编程的基石&#xff0c;它们定义了可以操作的数据的种类。Python是一种动态类型语言&#xff0c;意味着你不需要显式地声明变量的类型&#xff1b;Python解释器会自动推断出变量所存储数据的类型。Python提供了多种内置数据类型&#xff0c;这…

c++类和对象(3):默认成员函数(下)

1.拷贝构造函数 如果⼀个构造函数的第⼀个参数是自身类类型的引用&#xff0c;且任何额外的参数都有默认值&#xff0c;则此构造函数也叫做拷贝构造函数&#xff0c;也就是说拷贝构造是⼀个特殊的构造函数。 c规定&#xff1a;类类型的传值传参必须用拷贝构造 1.1拷贝构造函数…

OpenAI 刚刚推出 o1 大模型!!突破LLM极限

北京时间 9 月 13 日午夜&#xff0c;OpenAI 正式发布了一系列全新的 AI 大模型&#xff0c;专门用于应对复杂问题。 这一新模型的出现代表了一个重要突破&#xff0c;其具备的复杂推理能力远远超过了以往用于科学、代码和数学等领域的通用模型&#xff0c;能够解决比之前更难的…