使用环境:Ubuntu16.04+Qtcreator-5.0.0
强烈推荐贼棒的Qt学习资料:https://subingwen.cn/qt/
Qt 中多线程的使用:https://subingwen.cn/qt/thread/
视频教程:https://www.bilibili.com/video/BV1iN411f7dY?p=1
目录
关于Qt中的emit()函数
关于Qt中的元对象系统
在mythread.h文件中声明信号函数mess()和Display(const cv::Mat *image, int index),运行之后,会自动生成moc_mythread.cpp文件
https://blog.csdn.net/m0_45867846/article/details/107585903
https://blog.csdn.net/weixin_52511809/article/details/115549335
1.Qt中使用多线程的必要性
使用多线程会减少界面的卡顿,让程序的运行更加流畅,提升用户的使用体验。举例:正常启动一个Qt程序,默认只有一个线程,我们在拖动UI界面的时候,窗口也会随着鼠标拖动并且显示其内容,倘若现在线程正在执行一个复杂的逻辑程序,这时再拖动窗口时,窗口并不会随着鼠标的拖动而移动,会显示无响应的状态。此时就需要使用多线程来处理,让一个子线程来处理复杂的逻辑程序,让主线程来实时显示窗口的内容,两个线程之间各不影响,子线程处理完毕后会把结果发送给主线程,最后再通过主线程将结果显示在窗口中。
在进行桌面应用程序开发的时候, 假设应用程序在某些情况下需要处理比较复杂的逻辑, 如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。这种情况下就需要使用多线程,其中一个线程处理窗口事件,其他线程进行逻辑运算,多个线程各司其职,不仅可以提高用户体验还可以提升程序的执行效率。
1.1 使用时的注意点
在 qt 中使用了多线程,有些事项是需要额外注意的:
- 默认的线程在Qt中称之为窗口线程,也叫主线程,负责窗口事件处理或者窗口控件数据的更新。
- 子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理。
- 主线程和子线程之间如果要进行数据的传递,需要使用Qt中的信号槽机制。
2.线程类 QThread
通过Qt帮助文档,查询QThread类相关信息,包括公共函数、公共槽函数、信号、静态成员函数等。
2.1 常用共用成员函数
- QThread 类常用 API
- 构造函数——指定一个父对象即可(父类和父对象的区别:父对象是按照从属关系来的,在创建一个对象的时候,Qt是通过对象树来完成内存回收的。假设在对象A下面有一个对象B,则对象B是对象A的子对象,在析构对象A的时候,程序首先会析构对象B。对象树中成员之间的关系并不是都是继承关系(父类中是按照继承关系来的)。)
QThread::QThread(QObject *parent = Q_NULLPTR);
- 判断线程的状态
// 判断线程中的任务是不是处理完毕了,初始false,start()之后进入run(),只要是run()没有退出就是true
bool QThread::isFinished() const;
// 判断子线程是不是在执行任务,正在执行返回true,初始为false,在run()执行完一次之后为true,再次start()后重置为false
bool QThread::isRunning() const;
// 在耗时操作中使用isInterruptionRequested()来判断是否请求终止线程,如果没有,则一直运行;当希望终止线程的时候,调用requestInterruption()即可。
// 这个是Qt内部通过锁实现的(安全的),通过requestInterruption()请求中断和wait()使得线程退出(前提是run()中用了isInterruptionRequested()判断)
// 这个返回值只有再requestInterruption()后,且wait()之间为true,其他时间都是false
bool QThread::isInterruptionRequested() const;
- Qt中线程的优先级,通过枚举类型来描述。如果把某个子线程的优先级调低了,那么该线程抢到CPU时间片的概率就降低了。
// 得到当前线程的优先级
Priority QThread::priority() const;
// 设置线程的优先级
void QThread::setPriority(Priority priority);
// 优先级:
QThread::IdlePriority 0 --> 最低的优先级
QThread::LowestPriority 1
QThread::LowPriority 2
QThread::NormalPriority 3
QThread::HighPriority 5
QThread::HighestPriority 6
QThread::TimeCriticalPriority 7 --> 最高的优先级
QThread::InheritPriority 8 --> 表示创建的子线程的优先级跟随创建它的主线程,子线程和其父线程的优先级相同, 默认是这个
- 退出当前线程和等待当前线程的结束——搭配使用
// 退出线程, 停止底层的事件循环
// 退出线程的工作函数
void QThread::exit(int returnCode = 0);
// 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是等待任务完成, 然后退出线程, 一般情况下会在 exit() 后边调用这个函数
bool QThread::wait(unsigned long time = ULONG_MAX);
2.2 信号和槽函数
- 信号
// 和调用 exit() 效果是一样的
// 调用这个函数之后, 再调用 wait() 函数
[slot] void QThread::quit();
// 启动子线程
[slot] void QThread::start(Priority priority = InheritPriority);
// 线程退出, 可能是会马上终止线程,不再需要调用wait()函数了, 一般情况下不使用这个函数
[slot] void QThread::terminate();
- 槽函数
// 线程中执行的任务完成了, 发出该信号
// 任务函数中的处理逻辑执行完毕了
[signal] void QThread::finished();
// 开始工作之前发出这个信号, 一般不使用
[signal] void QThread::started();
2.3 静态函数
// 返回一个指向管理当前执行线程的QThread的指针
[static] QThread *QThread::currentThread();
// 返回可以在系统上运行的理想线程数 == 和当前电脑的 CPU 核心数相同
[static] int QThread::idealThreadCount();
// 线程休眠函数
[static] void QThread::msleep(unsigned long msecs); // 单位: 毫秒
[static] void QThread::sleep(unsigned long secs); // 单位: 秒
[static] void QThread::usleep(unsigned long usecs); // 单位: 微秒
2.4 任务处理函数
// 子线程要处理什么任务, 需要写到 run() 中
[virtual protected] void QThread::run();
这个 run()
是一个虚函数,如果想让创建的子线程执行某个任务,需要写一个子类让其继承 QThread
,并且在子类中重写父类的 run()
方法,函数体就是对应的任务处理流程。另外,这个函数是一个受保护的成员函数,不能够在类的外部调用,如果想要让线程执行这个函数中的业务流程,需要通过当前线程对象调用槽函数 start()
启动子线程,当子线程被启动,这个 run()
函数也就在线程内部被调用了。
3.Qt中线程的使用方式一
3.1 操作步骤
- 需要创建一个线程类的子类,让其继承 QT 中的线程类 QThread,比如在 mythread.h 中写下:
class MyThread:public QThread
{
......
public:
MyThread();
~MyThread();
void run();
}
- 重写父类的 run () 方法,在该函数内部编写子线程要处理的具体的业务流程,比如在 mythread.cpp 中写下:
class MyThread:public QThread
{
......
protected:
void run()
{
........
}
}
- 在主线程中创建子线程对象,new 一个就可以了,比如在主函数中 widget.h 中写下:
MyThread * subThread = new MyThread;
- 在主线程中启动子线程,调用 start () 方法,比如在 widget.cpp 中调用启动子线程任务:
subThread->start();
不能在类的外部调用 run() 方法启动子线程,在外部调用 start() 相当于让 run() 开始运行。
PS:当子线程别创建出来之后,父子线程之间的通信可以通过信号槽的方式,注意事项:
- 在 Qt 中在子线程中不要操作程序中的窗口类型对象,不允许,如果操作了程序就挂了。
- 只有主线程才能操作程序中的窗口对象,默认的线程就是主线程,自己创建的就是子线程
3.2 举例一
假如只有一个线程,让其一直数数,会发现数字并不会在窗口中时时更新,并且这时候如果用户使用鼠标拖动窗口,就会出现无响应的情况,使用多线程就不会出现这样的现象了。
- 不使用多线程
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pbn_start, &QPushButton::clicked, this, [=]() {
int num = 0;
while (1) {
num++;
if (num == 10000000) {
break;
}
ui->lbl_show->setNum(num);
}
});
}
MainWindow::~MainWindow()
{
delete ui;
}
效果:
点击开始按钮,会发现数字并不会在窗口中时时更新,并且这时候如果用户使用鼠标拖动窗口,就会出现无响应的情况,在过了一会儿后界面上显示9999999。
- 使用多线程
思路:在界面窗口中,点击按钮开始在子线程中数数,让后通过信号槽机制将数据传递给 UI 线程,通过 UI 线程将数据更新到窗口中。
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
~MyThread();
protected:
void run();
signals:
// 自定义信号,传递数据
void curNumber(int num);
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
MyThread::MyThread()
{
}
MyThread::~MyThread()
{
}
void MyThread::run()
{
qDebug() << "当前线程对象的地址:" << QThread::currentThread();
int num = 0;
while (1) {
emit curNumber(num++);
if (num == 1000000) {
break;
}
QThread::usleep(1);
}
qDebug() << "run() 执行完毕,子程序退出...";
}
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();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "mythread.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug() << "主线程对象地址:" << QThread::currentThread();
// 创建子线程
MyThread *subThread = new MyThread;
connect(subThread, &MyThread::curNumber, this,
[=](int num) { ui->lbl_show->setNum(num); });
connect(ui->pbn_start, &QPushButton::clicked, this, [=]() {
// 启动子线程
subThread->start();
});
// 线程资源释放
connect(this, &MainWindow::destroyed, this, [=]() {
subThread->quit();
subThread->wait();
subThread->deleteLater();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
效果:
点击开始按钮,会发现数字在窗口中时时更新,并且这用户使用鼠标拖动窗口时界面上面的数字也在更新。
- 这种在程序中添加子线程的方式是简单的,但是也有弊端,假设要在一个子线程中处理多个任务,所有的处理逻辑都需要写到run()函数中,这样该函数中的处理逻辑就会变得非常混乱,不太容易维护。
3.2 举例二
需求:主线程A中生成10000个随机数并进行显示, 子线程B中对主线程A中生成的随机数进行冒泡排序,子线程C中对主线程A中生成的随机数进行快速排序,两种排序方法不一样,并对两种方法的排序时间进行统计和分别显示。
思路:生成的随机数个数由主线程发送给子线程,子线程生成了随机数之后,将这些数据通过信号槽发送给主线程进行显示,(子线程不能直接把随机数初始化到ui界面上,子线程没有资格直接对窗口直接进行读写操作)
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QVector>
// 生成随机数的子线程类
class Generate : public QThread
{
Q_OBJECT
public:
Generate();
~Generate();
// 从主线程中获取子线程中应该生成的随机数个数
void receNum(int num);
protected:
void run() override;
signals:
// 将子线程中生成的随机数通过信号发送给主线程进行显示
void sendArray(QVector<int> num);
private:
int m_num_;
};
// 对随机进行冒泡排序的子线程类
class BubbleSort : public QThread
{
Q_OBJECT
public:
BubbleSort();
~BubbleSort();
// 从主线程中获取需要进行冒泡排序的随机数
void receArray(QVector<int> list);
protected:
void run() override;
signals:
// 子线程进行冒泡排序完成后,将排序好了的数发送给主线程进行显示
void finish(QVector<int> num);
private:
QVector<int> m_list_;
};
// 对随机进行快速排序的子线程类
class QuickSort : public QThread
{
Q_OBJECT
public:
QuickSort();
~QuickSort();
// 从主线程中获取需要进行快速排序的随机数数
void receArray(QVector<int> list);
protected:
void run() override;
signals:
// 子线程进行快速排序完成后,将排序好了的数发送给主线程进行显示
void finish(QVector<int> num);
private:
void quickSort(QVector<int> &list, int l, int r);
private:
QVector<int> m_list_;
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
#include <QElapsedTimer>
Generate::Generate()
{
}
Generate::~Generate()
{
}
void Generate::receNum(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(qrand() % 100000);
}
int milsec = time.elapsed();
qDebug() << "生成" << m_num_ << "个随机数总共用时:" << milsec << "毫秒";
// 将生成好的随机数通过信号发送给主线程
emit sendArray(list);
}
BubbleSort::BubbleSort()
{
}
BubbleSort::~BubbleSort()
{
}
void BubbleSort::receArray(QVector<int> list)
{
m_list_ = list;
}
void BubbleSort::run()
{
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(m_list_);
}
QuickSort::QuickSort()
{
}
QuickSort::~QuickSort()
{
}
void QuickSort::receArray(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(m_list_);
}
void QuickSort::quickSort(QVector<int> &list, int l, int r)
{
if (l < r) {
int i = l, j = r;
int x = list[l];
while (i < j) {
while ((i < j) && (list[j] >= x)) {
j--;
}
if (i < j) {
list[i++] = list[j];
}
while (i < j && list[i] < x) {
i++;
}
if (i < j) {
list[j--] = list[i];
}
}
list[i] = x;
quickSort(list, l, i - 1);
quickSort(list, i + 1, r);
}
}
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
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "mythread.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 1.创建子线程对象
Generate *gen = new Generate;
BubbleSort *bubble = new BubbleSort;
QuickSort *quick = new QuickSort;
// 信号发送者是当前窗口this对象,操作是发出信号starting,信号接收者是子线程对象gen,操作是调用槽函数recvNum。
// 执行当前的connet操作后,只要是emit starting(10000)这个信号发出去后,
// 子线程的recvNum就能接收到starting(10000)发送出去的实参10000。
connect(this, &MainWindow::starting, gen, &Generate::receNum);
// 2.启动子线程
connect(ui->pbn_start, &QPushButton::clicked, this, [=]() {
// 主线程发送信号
emit starting(10000);
gen->start();
});
// 将生成的随机数由gen对象传给两个子线程
connect(gen, &Generate::sendArray, bubble, &BubbleSort::receArray);
connect(gen, &Generate::sendArray, quick, &QuickSort::receArray);
// 3.接收子线程发送的数据
connect(gen, &Generate::sendArray, this, [=](QVector<int> list) {
bubble->start();
quick->start();
for (int i = 0; i < list.size(); i++) {
ui->lsw_random->addItem(QString::number(list.at(i)));
}
});
// 将两个排序子线程排序好了的随机数传送给主线程进行显示
connect(bubble, &BubbleSort::finish, this, [=](QVector<int> list) {
for (int i = 0; i < list.size(); i++) {
ui->lsw_bubble_sort->addItem(QString::number(list.at(i)));
}
});
connect(quick, &QuickSort::finish, this, [=](QVector<int> list) {
for (int i = 0; i < list.size(); i++) {
ui->lsw_quick_sort->addItem(QString::number(list.at(i)));
}
});
// 线程资源释放
connect(this, &MainWindow::destroyed, this, [=]() {
gen->quit();
gen->wait();
gen->deleteLater();
bubble->quit();
bubble->wait();
bubble->deleteLater();
quick->quit();
quick->wait();
quick->deleteLater();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
效果:
4.Qt中线程的使用方式二
Qt 提供的第二种线程的创建方式弥补了第一种方式的缺点,用起来更加灵活,但是这种方式写起来会相对复杂一些。
4.1 操作步骤
- 创建一个新的类,让这个类从 QObject 派生
class MyWork:public QObject
{
.......
}
- 在这个类中添加一个公共的成员函数,函数体就是我们要子线程中执行的业务逻辑,可以有多个working()
class MyWork:public QObject
{
public:
.......
// 函数名自己指定, 叫什么都可以, 参数可以根据实际需求添加
void working();
}
- 在主线程中创建一个 QThread 对象,这就是子线程的对象
QThread* sub = new QThread;
- 在主线程中创建工作的类对象(千万不要指定给创建的对象指定父对象)
MyWork* work = new MyWork(this); // error
MyWork* work = new MyWork; // ok
- 将 MyWork 对象移动到创建的子线程对象中,需要调用 QObject 类提供的 moveToThread() 方法
// void QObject::moveToThread(QThread *targetThread);
// 如果给work指定了父对象, 这个函数调用就失败了
// 提示: QObject::moveToThread: Cannot move objects with a parent
work->moveToThread(sub); // 移动到子线程中工作
- 启动子线程,调用 start(),这时候线程启动了,但是移动到线程中的对象并没有工作。
- 调用 MyWork 类对象的工作函数,让这个函数开始执行,这时候是在移动到的那个子线程中运行的
4.2 举例一
假如只有一个线程,让其一直数数,会发现数字并不会在窗口中时时更新,并且这时候如果用户使用鼠标拖动窗口,就会出现无响应的情况,使用多线程就不会出现这样的现象了。
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
~MyThread();
public:
// 工作函数
void working();
signals:
// 自定义信号,传递数据
void curNumber(int num);
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
MyThread::MyThread()
{
}
MyThread::~MyThread()
{
}
void MyThread::working()
{
qDebug() << "当前线程对象的地址:" << QThread::currentThread();
int num = 0;
while (1) {
emit curNumber(num++);
if (num == 1000000) {
break;
}
QThread::usleep(1);
}
qDebug() << "working() 执行完毕,子程序退出...";
}
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();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "mythread.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug() << "主线程对象地址:" << QThread::currentThread();
// 创建线程对象
QThread *sub = new QThread;
// 创建工作的类对象
// 千万不要指定给创建的对象指定父对象
// 如果指定了: QObject::moveToThread: Cannot move objects with a parent
MyThread *work = new MyThread;
// 将工作的类对象移动到创建的子线程对象中
work->moveToThread(sub);
// 启动线程
sub->start();
// 让工作的对象开始工作, 点击开始按钮, 开始工作
connect(ui->pbn_start, &QPushButton::clicked, work, &MyThread::working);
// 显示数据
connect(work, &MyThread::curNumber, this,
[=](int num) { ui->lbl_show->setNum(num); });
// 线程资源释放
connect(this, &MainWindow::destroyed, this, [=]() {
sub->quit();
sub->wait();
sub->deleteLater();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
效果:
和方式一的运行效果一致。
- 使用这种多线程方式,假设有多个不相关的业务流程需要被处理,那么就可以创建多个类似于 MyWork 的类,将业务流程放多类的公共成员函数中,然后将这个业务类的实例对象移动到对应的子线程中 moveToThread() 就可以了,这样可以让编写的程序更加灵活,可读性更强,更易于维护。
4.2 举例二
需求:主线程A中生成10000个随机数并进行显示, 子线程B中对主线程A中生成的随机数进行冒泡排序,子线程C中对主线程A中生成的随机数进行快速排序,两种排序方法不一样,并对两种方法的排序时间进行统计和分别显示。
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QVector>
// 生成随机数的子线程类
class Generate : public QObject
{
Q_OBJECT
public:
Generate();
~Generate();
void working(int num);
signals:
// 将子线程中生成的随机数通过信号发送给主线程进行显示
void sendArray(QVector<int> num);
};
// 对随机进行冒泡排序的子线程类
class BubbleSort : public QObject
{
Q_OBJECT
public:
BubbleSort();
~BubbleSort();
void working(QVector<int> list);
signals:
// 子线程进行冒泡排序完成后,将排序好了的数发送给主线程进行显示
void finish(QVector<int> num);
};
// 对随机进行快速排序的子线程类
class QuickSort : public QObject
{
Q_OBJECT
public:
QuickSort();
~QuickSort();
void working(QVector<int> list);
signals:
// 子线程进行快速排序完成后,将排序好了的数发送给主线程进行显示
void finish(QVector<int> num);
private:
void quickSort(QVector<int> &list, int l, int r);
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
#include <QElapsedTimer>
#include <QThread>
Generate::Generate()
{
}
Generate::~Generate()
{
}
void Generate::working(int num)
{
qDebug() << "生成随机数的线程的线程地址:" << QThread::currentThread();
QVector<int> list;
QElapsedTimer time;
time.start();
for (int i = 0; i < num; i++) {
list.push_back(qrand() % 100000);
}
int milsec = time.elapsed();
qDebug() << "生成" << num << "个随机数总共用时:" << milsec << "毫秒";
// 将生成好的随机数通过信号发送给主线程
emit sendArray(list);
}
BubbleSort::BubbleSort()
{
}
BubbleSort::~BubbleSort()
{
}
void BubbleSort::working(QVector<int> list)
{
qDebug() << "冒泡排序的线程的线程地址:" << QThread::currentThread();
QElapsedTimer time;
time.start();
// 冒泡排序
int temp;
for (int i = 0; i < list.size(); ++i) {
for (int j = 0; j < list.size() - i - 1; ++j) {
if (list[j] > list[j + 1]) {
temp = list[j];
list[j] = list[j + 1];
list[j + 1] = temp;
}
}
}
int milsec = time.elapsed();
qDebug() << "冒泡排序用时:" << milsec << "毫秒";
// 将冒泡排序好的随机数通过信号发送给主线程
emit finish(list);
}
QuickSort::QuickSort()
{
}
QuickSort::~QuickSort()
{
}
void QuickSort::working(QVector<int> list)
{
qDebug() << "快速排序的线程的线程地址:" << QThread::currentThread();
QElapsedTimer time;
time.start();
// 快速排序
quickSort(list, 0, list.size() - 1);
int milsec = time.elapsed();
qDebug() << "快速排序用时:" << milsec << "毫秒";
// 将快速排序好的随机数通过信号发送给主线程
emit finish(list);
}
void QuickSort::quickSort(QVector<int> &list, int l, int r)
{
if (l < r) {
int i = l, j = r;
int x = list[l];
while (i < j) {
while ((i < j) && (list[j] >= x)) {
j--;
}
if (i < j) {
list[i++] = list[j];
}
while (i < j && list[i] < x) {
i++;
}
if (i < j) {
list[j--] = list[i];
}
}
list[i] = x;
quickSort(list, l, i - 1);
quickSort(list, i + 1, r);
}
}
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
mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "mythread.h"
#include <QDebug>
#include <QThread>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
// 1.创建子线程对象
QThread *t1 = new QThread;
QThread *t2 = new QThread;
QThread *t3 = new QThread;
// 2.创建任务类的对象
Generate *gen = new Generate;
BubbleSort *bubble = new BubbleSort;
QuickSort *quick = new QuickSort;
// 3.将任务对象移动到某个子线程中
gen->moveToThread(t1);
bubble->moveToThread(t2);
quick->moveToThread(t3);
// 信号发送者是当前窗口this对象,操作是发出信号starting,信号接收者是子线程对象gen,操作是调用函数working。
connect(this, &MainWindow::starting, gen, &Generate::working);
// 启动子线程
connect(ui->pbn_start, &QPushButton::clicked, this, [=]() {
// 主线程发送信号
emit starting(10000);
t1->start();
});
// 将生成的随机数由gen对象传给两个子线程
connect(gen, &Generate::sendArray, bubble, &BubbleSort::working);
connect(gen, &Generate::sendArray, quick, &QuickSort::working);
// 接收子线程发送的数据
connect(gen, &Generate::sendArray, this, [=](QVector<int> list) {
t2->start();
t3->start();
for (int i = 0; i < list.size(); i++) {
ui->lsw_random->addItem(QString::number(list.at(i)));
}
});
// 将两个排序子线程排序好了的随机数传送给主线程进行显示
connect(bubble, &BubbleSort::finish, this, [=](QVector<int> list) {
for (int i = 0; i < list.size(); i++) {
ui->lsw_bubble_sort->addItem(QString::number(list.at(i)));
}
});
connect(quick, &QuickSort::finish, this, [=](QVector<int> list) {
for (int i = 0; i < list.size(); i++) {
ui->lsw_quick_sort->addItem(QString::number(list.at(i)));
}
});
// 线程资源释放
connect(this, &MainWindow::destroyed, this, [=]() {
t1->quit();
t1->wait();
t1->deleteLater();
t2->quit();
t2->wait();
t2->deleteLater();
t3->quit();
t3->wait();
t3->deleteLater();
gen->deleteLater();
bubble->deleteLater();
quick->deleteLater();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
效果:
Qt 中线程池的使用:
https://subingwen.cn/qt/threadpool/