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

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仓库源码

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • linux_L2_linux删除文件
  • 第R3周:LSTM-火灾温度预测:3. nn.LSTM() 函数详解
  • web安卓逆向之必学HTML基础知识
  • 大数据新视界 --大数据大厂之 Cassandra 分布式数据库:高可用数据存储的新选择
  • 牛客周赛 Round 60 连点成线(哈希+模拟)
  • 数据结构之哈希表
  • Redis 与数据库数据一致性保证详解
  • 微服务实战系列之玩转Docker(十五)
  • Github 2024-09-16 开源项目周报 Top14
  • iOS 18 將在 9 月 16 日正式上線
  • 鸡蛋检测系统源码分享
  • leaflet【十】实时增加轨迹点轨迹回放效果实现
  • BSV区块链上的覆盖网络服务现已开放公测
  • mysql DBA常用的sql
  • 【JS逆向分析】某药品网站价格(Price)解密
  • Brief introduction of how to 'Call, Apply and Bind'
  • JavaScript设计模式系列一:工厂模式
  • Java的Interrupt与线程中断
  • JSONP原理
  • Logstash 参考指南(目录)
  • Spark RDD学习: aggregate函数
  • Xmanager 远程桌面 CentOS 7
  • XML已死 ?
  • 阿里云前端周刊 - 第 26 期
  • - 概述 - 《设计模式(极简c++版)》
  • 欢迎参加第二届中国游戏开发者大会
  • 聊一聊前端的监控
  • 码农张的Bug人生 - 初来乍到
  • 前端性能优化——回流与重绘
  • 前端自动化解决方案
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 手机端车牌号码键盘的vue组件
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 详解NodeJs流之一
  • 携程小程序初体验
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • 在Unity中实现一个简单的消息管理器
  • zabbix3.2监控linux磁盘IO
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • # C++之functional库用法整理
  • #define 用法
  • #NOIP 2014# day.2 T2 寻找道路
  • (1)(1.13) SiK无线电高级配置(五)
  • (2009.11版)《网络管理员考试 考前冲刺预测卷及考点解析》复习重点
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (python)数据结构---字典
  • (SpringBoot)第七章:SpringBoot日志文件
  • (二)Linux——Linux常用指令
  • (二)构建dubbo分布式平台-平台功能导图
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)ORM
  • (转)大型网站架构演变和知识体系
  • **PHP二维数组遍历时同时赋值
  • .NET CORE 第一节 创建基本的 asp.net core