QT 创建子线程
QT 创建子线程有几种方法:
- 创建子类,继承自
QThread
类,重写QThread::run()
函数,调用子类的QThread::start()
即可在子线程中执行。 QThread::create()
创建线程指针对象,调用指针线程的QThread::start()
即可在子线程中执行。- 调用
QObject::moveToThread()
到QThread
类对象,通过信号和槽触发执行子类处理函数。
注意:
moveToThread
方法必须使用信号和槽的触发方式,否则依然在调用线程中执行。- 线程的销毁注意子类资源的释放。
代码示例
- .h
class Thread : public QThread {
Q_OBJECT
public:
explicit Thread(QObject *parent = nullptr);
virtual ~Thread();
protected:
virtual void run() override;
};
class Worker : public QObject{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
virtual ~Worker();
void doWorker();
virtual bool event(QEvent *event) override;
signals:
void start();
};
- .cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
// 防止输入法中间输出内容
QTimer*timer = new QTimer;
connect(timer, &QTimer::timeout, this, [this, timer](){
timer->stop();
qDebug() << "\n====\n";
this->ThreadTest();
});
timer->start(1000);
}
MainWindow::~MainWindow() { delete ui; }
#define LOG_THREAD_FUNC qDebug().noquote().nospace() << "[0x" << (void*) QThread::currentThreadId() << "] : " << __PRETTY_FUNCTION__
#define LOG_THREAD qDebug().noquote().nospace() << "[0x" << (void*) QThread::currentThreadId() << "] : "
#define SLEEP_SECONES 3
static bool willRun = true; // THREAD_MOVE_TO_THREAD 示例中, Thread 作为承接线程会重复输出,需要手动跳过。
auto thread_doworker_slots = [](){
if(!willRun) { return; }
LOG_THREAD << "run in.";
QThread::sleep(SLEEP_SECONES);
LOG_THREAD << "run out.";
};
auto thread_started_slots = [](){ LOG_THREAD << "started."; };
auto thread_finished_slots = [](){
LOG_THREAD << "finished in.";
QThread::currentThread()->deleteLater(); // ok,但是不会析构子类资源
// thread->deleteLater(); // ok // 传入线程对象较为麻烦,暂时不用该方法
// sender()->deleteLater(); // crash
LOG_THREAD << "finished out.";
};
Thread::Thread(QObject *parent) : QThread(parent) { LOG_THREAD_FUNC; }
Thread::~Thread() { LOG_THREAD_FUNC; }
void Thread::run() { thread_doworker_slots(); }
Worker::Worker(QObject *parent) : QObject(parent) { LOG_THREAD_FUNC; }
Worker::~Worker() { LOG_THREAD_FUNC; }
void Worker::doWorker() {
LOG_THREAD << "Worker in.";
QThread::sleep(SLEEP_SECONES);
LOG_THREAD << "Worker out.";
}
bool Worker::event(QEvent *event) {
switch (event->type()) {
case QEvent::ThreadChange:
LOG_THREAD_FUNC << " case QEvent::ThreadChange.";
break;
default:
break;
}
return QObject::event(event);
}
void MainWindow::ThreadTest() {
// 使用指针对象可立即返回,并在执行结束后调用 deleteLater 进行资源销毁。 // 调用 QThread::wait 会阻塞返回
// thread->wait : [main]Thread -> [thread]run in -> [thread]run out -> [main]started.
// -> [main]finished in. -> [main]finished out. -> [main]~Thread -> [QWidget]show;
// no thread->wait : [main]Thread -> [thread]run in -> [main]started. -> [QWidget]show -> [thread]run out
// -> [main]finished in. -> [main]finished out. -> [main]~Thread ;
enum {
THREAD_OBJECT_WAIT, // 不推荐 // 创建线程对象,在函数中等待执行结果。失去创建线程的意义。
THREAD_POINT_DELETE_LATE, // 不推荐 // 创建线程指针对象,调用线程默认的deleteLater销毁线程指针,可销毁子类资源。
THREAD_POINT_DELETE_SLOT_PUBLIC, // 不推荐 // 创建线程指针对象,调用公共自定义函数销毁线程资源 // 暂时无法销毁子类资源
THREAD_POINT_DELETE_SLOT_SELF, // 推荐 // 创建线程指针对象,调用专用自定义函数销毁线程资源,可销毁子类资源。
THREAD_CREATE, // 推荐 // 调用 create 创建线程对象
THREAD_MOVE_TO_THREAD // 推荐 // 调用 moveToThread 及信号和槽,由子进程执行操作
};
int test = 30;
switch (test) {
// Thread 对象,非指针,只能函数内等待销毁
case THREAD_OBJECT_WAIT: {
// 线程的生命周期需要在函数内销毁,会阻塞函数运行
Thread thread; // 创建对象
thread.start(); // 启动执行
thread.wait(); // 等待结束,否则崩溃 // 没有detach函数
// output:
// [0x0x6d2c] : Thread::Thread(QObject*)
// [0x0x1678] : run in.
// [0x0x1678] : run out.
// [0x0x6d2c] : virtual Thread::~Thread()
}
break;
// Thread 指针,立即返回
case THREAD_POINT_DELETE_LATE: {
Thread *thread = new Thread;
connect(thread, &Thread::started , thread, thread_started_slots);
connect(thread, &Thread::finished, thread, &Thread::deleteLater); // 会调用析构
thread->start();
// output:
// [0x0x4858] : Thread::Thread(QObject*)
// [0x0x6afc] : run in.
// [0x0x4858] : started.
// [0x0x6afc] : run out.
// [0x0x4858] : virtual Thread::~Thread()
}
break;
case THREAD_POINT_DELETE_SLOT_PUBLIC: {
qWarning().noquote().nospace() << "注意:该示例中传入的为父类,因此不会调用子类的析构函数。";
Thread *thread = new Thread;
connect(thread, &Thread::started , thread, thread_started_slots);
connect(thread, &Thread::finished, thread, thread_finished_slots); // 不会调用析构(未传入子类对象)
thread->start();
// output:
// [0x0x42b8] : Thread::Thread(QObject*)
// [0x0x6374] : run in.
// [0x0x42b8] : started.
// [0x0x6374] : run out.
// [0x0x42b8] : finished in.
// [0x0x42b8] : finished out.
}
break;
case THREAD_POINT_DELETE_SLOT_SELF: {
Thread *thread = new Thread;
connect(thread, &Thread::started , thread, thread_started_slots);
connect(thread, &Thread::finished, thread, [thread](){
LOG_THREAD << "finished in.";
thread->deleteLater(); // ok // 会调用析构
LOG_THREAD << "finished out.";
});
thread->start();
// output:
// [0x0x70a0] : Thread::Thread(QObject*)
// [0x0x4e78] : run in.
// [0x0x70a0] : started.
// [0x0x4e78] : run out.
// [0x0x70a0] : finished in.
// [0x0x70a0] : finished out.
// [0x0x70a0] : virtual Thread::~Thread()
}
break;
// Thread::create
case THREAD_CREATE: {
QThread* thread = QThread::create(thread_doworker_slots);
connect(thread, &Thread::started , thread, thread_started_slots);
connect(thread, &Thread::finished, thread, [thread](){
LOG_THREAD << "finished in.";
thread->deleteLater(); // ok // 会调用析构
LOG_THREAD << "finished out.";
});
thread->start();
// output:
// [0x0x5640] : run in.
// [0x0x7ba8] : started.
// [0x0x5640] : run out.
// [0x0x7ba8] : finished in.
// [0x0x7ba8] : finished out.
}
break;
case THREAD_MOVE_TO_THREAD: {
// 移动到主线程 : myObject->moveToThread(QApplication::instance()->thread());
willRun = false;
Thread*thread = new Thread;
connect(thread, &Thread::started , thread, thread_started_slots);
connect(thread, &Thread::finished, thread, [thread](){
LOG_THREAD << "finished in.";
thread->deleteLater(); // ok // 会调用析构
LOG_THREAD << "finished out.";
});
Worker*worker = new Worker;
LOG_THREAD_FUNC << " moveToThread start.";
LOG_THREAD_FUNC << " moveToThread " << (worker->moveToThread(thread) ? "success." : "false.");
thread->start(); // 没有开始执行实际内容
LOG_THREAD_FUNC << " before doWorker.";
// worker->doWorker(); // 必须通过信号和槽的方式调用处理函数,处理函数才能在子线程中执行
connect(worker, &Worker::start, worker, &Worker::doWorker);
worker->start();
willRun = true;
// output:
// [0x0x73c8] : Thread::Thread(QObject*)
// [0x0x73c8] : Worker::Worker(QObject*)
// [0x0x73c8] : void MainWindow::ThreadTest() moveToThread start.
// [0x0x73c8] : virtual bool Worker::event(QEvent*) case QEvent::ThreadChange.
// [0x0x73c8] : void MainWindow::ThreadTest() moveToThread success.
// [0x0x73c8] : void MainWindow::ThreadTest() before doWorker.
// [0x0x7728] : run in.
// [0x0x73c8] : started.
// [0x0x7728] : run out.
// [0x0x73c8] : finished in.
// [0x0x73c8] : finished out.
// [0x0x73c8] : virtual Thread::~Thread()
}
break;
default:
LOG_THREAD_FUNC << " switch case default.";
break;
}
}