在QtC++中,有两种多线程编程的方式,接下来详细记录这两种使用方式。做做笔记,方便查看。
单线程的情况
在主线程中执行耗时操作,会导致界面卡住,未响应状态,影响用户体验。多线程异步操作可以解决这一问题。
void Widget::on_pushButton_clicked()
{
int num = 0;
while(1)
{
num ++;
if (num == 100000000)
{
break;
}
ui->label->setNum(num);
}
}
第一种方式 重写run函数
run适合于执行较简单的任务,run中不适合放置太多的代码。
第一步
创建线程类的子类,让其继承自QThread
创建好类文件后需要修改2个地方
第二步
重写父类的run方法第三步
调用start方法启动子线程
注意:
- 子线程中不可操作ui线程,比如子线程中设置标签的显示
示例代码
以下示例展示了ui线程怎么向子线程发送数据以及子线程怎么向ui线程发送数据,以实现主线程与子线程的通信。
生成随机数的子线程
// 生成随机数的子线程
class Generate : public QThread
{
Q_OBJECT
public:
explicit Generate(QObject *parent = nullptr);
protected:
void run() override;
public slots:
// 接收从ui界面发送过来的生成随机数的个数需求
void recvNumber(int num);
signals:
// 将生成的随机数发送给主线程
void sendArray(QVector<int> num);
private:
///
/// \brief m_num 存放从主线程发送过来的数值,数值为生成随机数的个数
/// 当主线程发送过来这个数据后,子线程要有一个槽函数来处理主线程发送过来的这个数值
///
///
int m_num;
};
Generate::Generate(QObject *parent) : QThread(parent)
{
}
// 重写run方法
void Generate::run()
{
qDebug() << "生成随机数的线程地址为:" << QThread::currentThread();
QVector<int> list;
QElapsedTimer startTime;
startTime.start();
for (int i = 0; i < m_num; i++)
{
// 存储到动态数组当中 随机生成100000 以内的随机数
list.push_back(qrand() % 100000);
}
// 计算执行所消耗的时间
int ms = startTime.elapsed();
qDebug() << "生成" << m_num << "个随机数用时" << ms << "毫秒";
emit sendArray(list);
}
// 接收数据
void Generate::recvNumber(int num)
{
this->m_num = num;
}
主线程代码
gen = new Generate;
// 连接子线程接收随机数个数的信号和槽
connect(this, &Widget::needRandomNumberCount, gen, &Generate::recvNumber);
// 连接主界面接收子线程发送数据的信号和槽
connect(gen, &Generate::sendArray, this, &Widget::recvRandomNumber);
void Widget::recvRandomNumber(QVector<int> list)
{
for (int i = 0; i < list.size(); i++)
{
ui->listWidget->addItem(QString::number(list.at(i)));
}
}
第二种方式 任务函数
第一步
创建一个任务类,该类必须继承自QObject第二步
在这个类中添加公共的成员函数,函数体就是执行的任务第三步
在主线程中创建一个QThread对象,这就是子线程对象第四步
在主线程中创建工作的类对象
注意: 不能给创建的任务对象指定父对象,致命提示!!!第五步
将任务对象移动到创建的子线程对象中第六步
调用start方法启动子线程,
注意: 这个时候线程启动了,但是移动到线程中的对象并没有工作第七步
调用任务对象的工作函数,让这个函数开始执行,任务才算真的执行了
示例代码
创建任务类,继承自QObject
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
// 任务类
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
public:
// void recvNum(int num);
// 任务函数 可以携带参数的
void working(int num);
void startedSlots();
signals:
void sendArray(QVector<int> num);
private:
int m_num;
};
#endif // MYTHREAD_H
子线程生成随机数的任务函数,可以有多个任务函数
#include "mythread.h"
#include <QElapsedTimer>
#include <QDebug>
#include <QThread>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
}
void MyThread::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 ms = time.elapsed();
qDebug() << "生成" << num << "个随机数总共用时:" << ms << "毫秒";
emit sendArray(list);
}
void MyThread::startedSlots()
{
qDebug() << "1";
}
主线程 ui线程
#include "widget.h"
#include "ui_widget.h"
#include "mythread.h"
#include <QThread>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 创建任务类对象
MyThread *work = new MyThread;
// 创建子线程对象
thread = new QThread;
// 将任务对象移动到子线程中
work->moveToThread(thread);
connect(thread, &QThread::started, work, &MyThread::startedSlots);
connect(this, &Widget::starting, work, &MyThread::working);
connect(work, &MyThread::sendArray, this, [=](QVector<int> list){
for (int i = 0; i < list.size(); i++)
{
ui->listWidget->addItem(QString::number(list.at(i)));
}
});
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
emit starting(10000);
thread->start();
}
运行结果
上边的代码没有释放和清理资源。这是很重要的一步操作
释放和清理资源
界面销毁时有一个destoryed信号,连接该信号进行释放即可
connect(this, &Widget::destroyed, this, [=]()
{
// 安全退出
thread->quit();
thread->wait();
thread->deleteLater();
work->deleteLater();
});