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