概述
QThread类提供了一个与平台无关的管理线程的方法。一个QThread对象管理一个线程。QThread的执行从run()函数的执行开始,在Qt自带的QThread类中,run()函数通过调用exec()函数来启动事件循环机制,并且在线程内部处理Qt的事件。在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,从而让主界面能及时响应用户的请求操作。
QThread的使用方法有如下两种:
- QObject::moveToThread()
- 继承QThread类
QObject::moveToThread()
- 定义一个继承于QObject的worker类,在worker类中定义一个槽slot函数doWork(),这个函数中定义线程需要做的工作。
- 在要使用线程的controller类中,新建一个QThread的对象和woker类对象,使用moveToThread()方法将worker对象的事件循环全部交由QThread对象处理。
- 建立相关的信号函数和槽函数进行连接,然后发出信号触发QThread的槽函数,使其执行工作。
首先新建一个work类,该类重点在于其doWork槽函数,这个函数定义了线程需要做的工作,需要向其发送信号来触发。Wrok类的头文件中定义了全部函数.
Work.h
// work定义了线程要执行的工作
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include<QDebug>
#include<QThread>
class Worker :public QObject
{
Q_OBJECT
public:
Worker(QObject* parent = nullptr) {}
public slots:
// doWork定义了线程要执行的操作
void doWork(int parameter)
{
qDebug() << "receive the execute signal---------------------------------";
qDebug() << " current thread ID:" << QThread::currentThreadId();
++parameter;
// 发送结束信号
emit resultReady(parameter);
}
// 线程完成工作时发送的信号
signals:
void resultReady(const int result);
};
#endif // WORKER_H
Controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include<QThread>
#include<QDebug>
#include "Worker.h"
// controller用于启动线程和处理线程执行结果
class Controller : public QObject
{
Q_OBJECT
public:
Controller(QObject *parent = nullptr)
{
Worker *worker = new Worker;
//调用moveToThread将该任务交给workThread
worker->moveToThread(&workerThread);
//operate信号发射后启动线程工作
connect(this, SIGNAL(operate(const int)), worker, SLOT(doWork(int)));
//该线程结束时销毁
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
//线程结束后发送信号,对结果进行处理
connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));
//启动线程
workerThread.start();
//发射信号,开始执行
qDebug() << "emit the signal to execute!---------------------------------";
qDebug() << " current thread ID:" << QThread::currentThreadId() << '\n';
emit operate(0);
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
// 处理线程执行的结果
void handleResults(const int rslt)
{
qDebug() << "receive the resultReady signal---------------------------------";
qDebug() << " current thread ID:" << QThread::currentThreadId() << '\n';
qDebug() << " the last result is:" << rslt;
}
signals:
// 发送信号触发线程
void operate(const int);
private:
QThread workerThread;
};
#endif // CONTROLLER_H
在需要使用的地方定义一个Controller c;
结果:
继承QThread的方法
方法描述
- 自定义一个继承QThread的类MyThread,重载MyThread中的run()函数,在run()函数中写入需要执行的工作.
- 调用start()函数来启动线程。
继承QThread的例子
首先写MyThread类,该类继承于QThread,该类中自定义了信号槽和重写了run函数。头文件如下:
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
#include<QDebug>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread(QObject* parent = nullptr);
//自定义发送的信号
signals:
void myThreadSignal(int);
//自定义槽
public slots:
void myThreadSlot(int);
protected:
void run() override;
};
#endif // MYTHREAD_H
MyThread.cpp
#include "mythread.h"
MyThread::MyThread(QObject *parent)
{
}
void MyThread::run()
{
qDebug() << "myThread run() start to execute";
qDebug() << " current thread ID:" << QThread::currentThreadId() << '\n';
int count = 0;
++count;
// 发送结束信号
emit myThreadSignal(count);
exec();
}
void MyThread::myThreadSlot(int val)
{
qDebug() << "myThreadSlot() start to execute";
qDebug() << " current thread ID:" << QThread::currentThreadId() << '\n';
//
int count = 0;
++count;
}
Controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include<QThread>
#include<QDebug>
#include "MyThread.h"
// controller用于启动线程和处理线程执行结果
class Controller : public QObject
{
Q_OBJECT
public:
Controller(QObject *parent = nullptr)
{
myThrd = new MyThread;
connect(myThrd, &MyThread::myThreadSignal, this, &Controller::handleResults);
// 该线程结束时销毁
connect(myThrd, &QThread::finished, this, &QObject::deleteLater);
connect(this, &Controller::operate, myThrd, &MyThread::myThreadSlot);
// 启动该线程
myThrd->start();
QThread::sleep(1);
emit operate(999);
}
~Controller() {
myThrd->quit();
myThrd->wait();
}
public slots:
// 处理线程执行的结果
void handleResults(int rslt)
{
qDebug() << "receive the resultReady signal---------------------------------";
qDebug() << " current thread ID:" << QThread::currentThreadId() << '\n';
qDebug() << " the last result is:" << rslt;
}
signals:
// 发送信号触发线程
void operate( int);
private:
MyThread* myThrd;
};
#endif // CONTROLLER_H
在需要执行的地方定义 Controller c;
通过自定义一个继承QThread的类,实例化该类的对象,重载run()函数为需要做的工作。然后在需要的地方调用start函数来执行run函数中的任务。从主函数触发了一个信号,对应于子线程的槽,子线程的槽函数中打印当前执行的线程的编号,可以看到,执行子线程的槽函数的线程编号却是主线程的编号。
两种方法的比较
moveToThread方法,是把我们需要的工作全部封装在一个类中,将每个任务定义为一个的槽函数,再建立触发这些槽的信号,然后把信号和槽连接起来,最后将这个类调用moveToThread方法交给一个QThread对象,再调用QThread的start()函数使其全权处理事件循环。于是,任何时候我们需要让线程执行某个任务,只需要发出对应的信号就可以。其优点是我们可以在一个worker类中定义很多个需要做的工作,然后发出触发的信号线程就可以执行。相比于子类化的QThread只能执行run()函数中的任务,moveToThread的方法中一个线程可以做很多不同的工作(只要发出任务的对应的信号即可)。
子类化QThread的方法,就是重写了QThread中的run()函数,在run()函数中定义了需要的工作。这样的结果是,我们自定义的子线程调用start()函数后,便开始执行run()函数。如果在自定义的线程类中定义相关槽函数,那么这些槽函数不会由子类化的QThread自身事件循环所执行,而是由该子线程的拥有者所在线程(一般都是主线程)来执行。
源码分析
class Q_CORE_EXPORT QThread : public QObject
{
Q_OBJECT
public:
static Qt::HANDLE currentThreadId() Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION;
static QThread *currentThread();
static int idealThreadCount() Q_DECL_NOTHROW;
static void yieldCurrentThread();
explicit QThread(QObject *parent = Q_NULLPTR);
~QThread();
//调度级别
enum Priority {
IdlePriority,
LowestPriority,
LowPriority,
NormalPriority,
HighPriority,
HighestPriority,
TimeCriticalPriority,
InheritPriority
};
void setPriority(Priority priority);
Priority priority() const;
bool isFinished() const;
bool isRunning() const;
void requestInterruption();
bool isInterruptionRequested() const;
void setStackSize(uint stackSize);
uint stackSize() const;
void exit(int retcode = 0);
QAbstractEventDispatcher *eventDispatcher() const;
void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);
bool event(QEvent *event) Q_DECL_OVERRIDE;
int loopLevel() const;
public Q_SLOTS:
void start(Priority = InheritPriority);//开始函数
void terminate();
void quit();
public:
// default argument causes thread to block indefinetely
bool wait(unsigned long time = ULONG_MAX);
static void sleep(unsigned long);
static void msleep(unsigned long);
static void usleep(unsigned long);
Q_SIGNALS:
void started(QPrivateSignal);
void finished(QPrivateSignal);
protected:
virtual void run();
int exec();
static void setTerminationEnabled(bool enabled = true);
protected:
QThread(QThreadPrivate &dd, QObject *parent = Q_NULLPTR);
private:
Q_DECLARE_PRIVATE(QThread)
friend class QCoreApplication;
friend class QThreadData;
};
C:\Qt\5.9.8\Src\qtbase\src\corelib\thread\qthread_win.cpp
start
void QThread::start(Priority priority)
{
Q_D(QThread);
QMutexLocker locker(&d->mutex);
if (d->isInFinish) {
locker.unlock();
wait();
locker.relock();
}
if (d->running)
return;
d->running = true;
d->finished = false;
d->exited = false;
d->returnCode = 0;
d->interruptionRequested = false;
......
// MSVC -MT or -MTd build
d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
this, CREATE_SUSPENDED, &(d->id));
#else
// MSVC -MD or -MDd or MinGW build 调用windows原生接口创建线程,线程中的函数指针指向QThreadPrivate::start
d->handle = (Qt::HANDLE) CreateThread(NULL, d->stackSize, (LPTHREAD_START_ROUTINE)QThreadPrivate::start,
this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
#endif // Q_OS_WINRT
......
}
创建线程中的函数指针 QThreadPrivate::start
unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg)
{
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadData *data = QThreadData::get2(thr);
//1 创建线程的局部变量
qt_create_tls();
TlsSetValue(qt_current_thread_data_tls_index, data);
data->threadId.store(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
QThread::setTerminationEnabled(false);
{
QMutexLocker locker(&thr->d_func()->mutex);
data->quitNow = thr->d_func()->exited;
}
//2 创建事件分发器
if (data->eventDispatcher.load()) // custom event dispatcher set?
data->eventDispatcher.load()->startingUp();
else
createEventDispatcher(data);
//3 设置线程名称
#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) && !defined(Q_OS_WINRT)
// sets the name of the current thread.
QByteArray objectName = thr->objectName().toLocal8Bit();
qt_set_thread_name((HANDLE)-1,
objectName.isEmpty() ?
thr->metaObject()->className() : objectName.constData());
#endif
//4 发送当前线程启动信号
emit thr->started(QThread::QPrivateSignal());
QThread::setTerminationEnabled(true);
//5 调用QThread run函数
thr->run();
finish(arg);
return 0;
}
void QThread::run()
run(): 线程的起点。 调用 start() 后,新创建的线程调用此函数。 默认实现只是调用 exec()。您可以重新实现此功能以方便高级线程管理。
exec():进入事件循环,等待 exit() 被调用,返回值传递给 exit()。此函数旨在从 run() 中调用。 有必要 调用此函数开始事件处理。
void QThread::run()
{
(void) exec();
}
int QThread::exec()
{
Q_D(QThread);
QMutexLocker locker(&d->mutex);
d->data->quitNow = false;
if (d->exited) {
d->exited = false;
return d->returnCode;
}
locker.unlock();
//启动事件循环器
QEventLoop eventLoop;
int returnCode = eventLoop.exec();
locker.relock();
d->exited = false;
d->returnCode = -1;
return returnCode;
}
总结:当调用 QThread::start()接口时,在windows平台会使用原生api创建线程,并且函数指针是QThreadPrivate::start,在QThreadPrivate::start中会调用Thread::run函数,Thread::run调用exec()函数启动事件循环器eventLoop;
每一个QThread start都会默认开启一个事件循环eventLoop。
exit
告诉线程的事件循环以返回码退出。 调用此函数后,线程离开事件循环并从对 QEventLoop::exec() 的调用返回。
void QThread::exit(int returnCode)
{
Q_D(QThread);
QMutexLocker locker(&d->mutex);
d->exited = true;
d->returnCode = returnCode;
d->data->quitNow = true;
for (int i = 0; i < d->data->eventLoops.size(); ++i) {
QEventLoop *eventLoop = d->data->eventLoops.at(i);
eventLoop->exit(returnCode);
}
}