Qt 中的多线程除了通过继承 QThread
类,然后重写虚函数 run()
之外还有另一种方案:将要执行的复杂操作,放在一个继承自 QObject
的类中,然后使用 QObject::moveToThread(QThread*)
函数将工作对象的移动到创建的子线程中去执行。
工作对象类
该类继承自 QObject
主要是用来实现一些耗时复杂的操作,这些操作可能会阻塞主线程,因此需要在后面将其移到子线程中去执行。
1、MyWork.h
文件
#pragma once
#include <QObject>
class MyWork: public QObject
{
Q_OBJECT
public:
MyWork();
~MyWork();
signals:
/**
* 子线程用来向主线程发送信号.
*/
void SendMsg(QString msg);
public slots:
/**
* 复杂操作在槽函数中实现,然后通过信号槽来与主线程建立联系
* 以触发该复杂的槽函数执行。
*/
void DoSomething();
};
2、MyWork.cpp
文件
#include "MyWork.h"
#include <QThread>
MyWork::MyWork()
{
}
MyWork::~MyWork()
{
}
void MyWork::DoSomething()
{
SendMsg(QString("子线程id = %1").arg((int)QThread::currentThreadId()));
SendMsg("Begin Work ......");
QThread::sleep(6);
SendMsg("Work Finished.");
}
主线程类
将在主线程类中去创建子线程,和 工作对象,并将工作对象移动到子线程中,然后通过主程序的信号来触发位于子线程中的工作对象,让其执行一些复杂的操作。
1、QThreadTest.h
文件
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_QThreadTest.h"
#include <QThread>
#include "MyWork.h"
class QThreadTest : public QMainWindow
{
Q_OBJECT
public:
QThreadTest(QWidget *parent = Q_NULLPTR);
~QThreadTest();
signals:
/**
* 主线程通过触发信号,来让子线程开始执行工作.
*/
void BeginWork();
private slots:
/**
* 直接在主线程中进行耗时操作,会直接阻塞主UI线程,导致程序卡死.
*/
void OnBtn01Clicked();
/**
* @brief 通过点击Button来触发对应的信号,然位于子线程中的工作对象开始执行对应的函数
*/
void OnBtn02Clicked();
/**
* @brief 由于子线程不能直接操作UI,所以该函数用于将接受到的子线程内容显示在UI上
* @param [in] msg
*/
void GetWorkMsg(QString msg);
private:
Ui::QThreadTestClass ui;
QThread *subThread; // 创建子线程,用来将任务移动到该线程中去执行
};
2、QThreadTest.cpp
文件
#include "QThreadTest.h"
#include <QPushButton>
QThreadTest::QThreadTest(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
subThread = new QThread(this);
// 创建工作对象,不能为其指定父指针,否则后面无法将其移动子线程中去执行
MyWork* myWork = new MyWork();
// 将工作对象移动到子线程中去执行
myWork->moveToThread(subThread);
// 在子线程执行结束之后,将工作对象的资源进行释放
connect(subThread, &QThread::finished, myWork, &MyWork::deleteLater);
// 通过主线程的信号来触发子线程开始执行对应的工作
connect(this, &QThreadTest::BeginWork, myWork, &MyWork::DoSomething);
// 在子线程中的工作对象无法直接修改主线程的UI,因此需要通过信号槽的方式来进行更新
connect(myWork, &MyWork::SendMsg, this, &QThreadTest::GetWorkMsg);
// 启动子线程,此时只是子线程开始执行,但是子线程中的具体工作 DoSomething 还未执行
subThread->start();
connect(ui.btn01, &QPushButton::clicked, this, &QThreadTest::OnBtn01Clicked);
connect(ui.btn02, &QPushButton::clicked, this, &QThreadTest::OnBtn02Clicked);
}
QThreadTest::~QThreadTest()
{
// 创建的线程一定要进行释放,否则关闭程序的时候线程仍然在运行,此时会造成程序Crash
subThread->quit();
subThread->wait();
}
void QThreadTest::OnBtn01Clicked()
{
ui.textBrowser_01->append("Start do something ...");
QThread::sleep(5);
ui.textBrowser_01->append("Finishing this operation.");
}
void QThreadTest::OnBtn02Clicked()
{
ui.textBrowser_02->append(QString("主线程id = %1").arg((int)QThread::currentThreadId()));
emit BeginWork(); // 触发信号,子线程中开始执行工作对象的函数
}
void QThreadTest::GetWorkMsg(QString msg)
{
ui.textBrowser_02->append(msg);
}
【总结】
该方法的主要流程为:
- 创建一个类,如
MyWork
继承自QObject
。 - 在
MyWork
类中将复杂的操作放在槽函数中,后续会通过信号槽的方式来触发子线程执行该操作。 - 在
MyWork
类中定义信号槽,用于子线程向主线程更新数据或信息。 - 在主线程中创建
QThread
对象,和MyWork
类对象,并将 工作对象移动到子线程中。 - 分别建立信号槽:
- 子线程执行完工作后,释放工作对象。(此时子线程可能并未退出)
- 主线程的开始执行信号连接工作对象的槽函数。
- 子线程向主线程发送数据,用来更新UI或同步操作。
- 启动子线程,此时工作对象的复杂操作还未执行。
- 在主线程中某一时机下,触发开始在子线程中执行工作类中的槽函数,此时才会进行复杂操作的执行。