多线程使用场景
桌面开发中,主线程负责窗口类,其他线程负责应用程序处理,减少窗口卡顿的现象发生。多线程机制正好满足这个需求,提高用户体验和代码执行效率。
使用多线程,有些事项是需要额外注意的:
-
默认的线程在Qt中称之为窗口线程,也叫主线程,负责窗口事件处理或者窗口控件数据的更新
-
子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理
-
主线程和子线程之间如果要进行数据的传递,需要使用Qt中的
信号槽
机制
引用:爱编程的大丙
步入主题:
多线程实现机制:
这里直接使用多线程的第二种实现机制:虽然这种方式写起来会相对复杂一些,但是用起来更加灵活,其具体操作步骤如下:
1、创建新类
创建一个新的类,让这个类从 QObject 派生
class MyWork:public QObject
{
.......
}
2、添加公共成员函数
类中添加公共成员函数,函数体是子线程执行的业务逻辑
class MyWork:public QObject
{
public:
.......
// 函数名自己指定, 叫什么都可以, 参数可以根据实际需求添加
void working();
}
3、创建QThread对象
在主线程中创建一个 QThread 对象,这就是子线程的对象
QThread* sub = new QThread;
4、创建工作的类对象
在主线程中创建工作的类对象(千万不要指定给创建的对象指定父对象)
MyWork* work = new MyWork(this); // error
MyWork* work = new MyWork; // ok
5、将MyWork对象移动到创建的子线程对象中
将 MyWork 对象移动到创建的子线程对象中,需要调用 QObject 类提供的 moveToThread() 方法
// void QObject::moveToThread(QThread *targetThread);
// 如果给work指定了父对象, 这个函数调用就失败了
// 提示: QObject::moveToThread: Cannot move objects with a parent
work->moveToThread(sub); // 移动到子线程中工作
6、启动子线程
启动子线程,调用 start(), 这时候线程启动了,但是移动到线程中的对象并没有工作
7、调用 MyWork 类对象的工作函数
调用 MyWork 类对象的工作函数,让这个函数开始执行,这时候是在移动到的那个子线程中运行的
示例代码
mywork.h
#ifndef MYWORK_H
#define MYWORK_H
#include <QObject>
class MyWork : public QObject
{
Q_OBJECT
public:
explicit MyWork(QObject *parent = nullptr);
// 工作函数
void working();
signals:
void curNumber(int num);
public slots:
};
#endif // MYWORK_H
mywork.cpp
#include "mywork.h"
#include <QDebug>
#include <QThread>
MyWork::MyWork(QObject *parent) : QObject(parent)
{
}
void MyWork::working()
{
qDebug() << "当前线程对象的地址: " << QThread::currentThread();
int num = 0;
while(1)
{
emit curNumber(num++);
if(num == 10000000)
{
break;
}
QThread::usleep(1);
}
qDebug() << "run() 执行完毕, 子线程退出...";
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QThread>
#include "mywork.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
MyWork* work = new MyWork;
// 将工作的类对象移动到创建的子线程对象中
work->moveToThread(sub);
// 启动线程
sub->start();
// 让工作的对象开始工作, 点击开始按钮, 开始工作
connect(ui->startBtn, &QPushButton::clicked, work, &MyWork::working);
// 显示数据
connect(work, &MyWork::curNumber, this, [=](int num)
{
ui->label->setNum(num);
});
}
MainWindow::~MainWindow()
{
delete ui;
}
现象
点击开始按钮之后,开始计数操作
使用这种多线程方式,假设有多个不相关的业务流程需要被处理,那么就可以创建多个类似于 MyWork 的类,将业务流程放多类的公共成员函数中,然后将这个业务类的实例对象移动到对应的子线程中 moveToThread() 就可以了,这样可以让编写的程序更加灵活,可读性更强,更易于维护。