一、用法简介
1.工作类
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
~Worker();
public slots:
void doWork(void);
};
Worker::Worker(QObject *parent) : QObject(parent)
{
}
Worker::~Worker()
{
}
void Worker::doWork()
{
qDebug() << "current thread id: " << QThread::currentThreadId();
}
2.控制类
class Controller : public QObject
{
Q_OBJECT
public:
explicit Controller(QObject *parent = nullptr);
~Controller();
//启动工作线程
void start();
private:
Worker *m_worker;
QThread *m_thread;
};
Controller::Controller(QObject *parent) : QObject(parent)
{
m_worker = new Worker;
m_thread = new QThread(this);
m_worker->moveToThread(m_thread);
connect(m_thread,&QThread::finished,m_worker,&Worker::deleteLater);
connect(m_thread,&QThread::started,m_worker,&Worker::doWork);
}
Controller::~Controller()
{
}
void Controller::start()
{
m_thread->start();
}
- 使用
int main(int argc,char *argv[])
{
QApplication a(argc, argv);
Controller controller;
controller.start();
return a.exec();
}
二、源码分享
1.Qt官方推荐使用类似上面工作类的方式,来使用线程,将耗时任务放在Worker的槽函数中,使用QObject::moveToThread()将Worker类的线程亲和性转移到工作线程中,然后通过connet线程类QThrad的started()信号到doWork(),在上述示例中,作为线程管理类QThread的实例m_thread参与的是主线程的事件循环,工作类m_worker在执行moveToThread()后,参与的是m_thread所管理的新工作线程的事件循环,执行connect(m_thread,&QThread::started,m_worker,&Worker::doWork);时,使用的连接方式自动转换为Qt::QueuedConnection,也就是说doWork槽函数会在新的工作线程中执行。
2.首先看一下QThread的构造函数
//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread.cpp:414
QThread::QThread(QObject *parent)
: QObject(*(new QThreadPrivate), parent)//在构造线程数据类QThreadPrivate时,使用的是默认参数nullptr,
{
Q_D(QThread);
// fprintf(stderr, "QThreadData %p created for thread %p\n", d->data, this);
d->data->thread.storeRelaxed(this);//此处在QThreadData中记录线程指针,隶属于本线程的QObject对象在调用
//QObject::thread()接口时,返回的就是该指针
}
QThreadPrivate::QThreadPrivate(QThreadData *d)
: QObjectPrivate(), running(false), finished(false),
isInFinish(false), interruptionRequested(false),
exited(false), returnCode(-1),
stackSize(0), priority(QThread::InheritPriority), data(d)
{
// INTEGRITY doesn't support self-extending stack. The default stack size for
// a pthread on INTEGRITY is too small so we have to increase the default size
// to 128K.
#ifdef Q_OS_INTEGRITY
stackSize = 128 * 1024;
#elif defined(Q_OS_RTEMS)
static bool envStackSizeOk = false;
static const int envStackSize = qEnvironmentVariableIntValue("QT_DEFAULT_THREAD_STACK_SIZE", &envStackSizeOk);
if (envStackSizeOk)
stackSize = envStackSize;
#endif
#if defined (Q_OS_WIN)
handle = 0;
id = 0;
waiters = 0;
terminationEnabled = true;
terminatePending = false;
#endif
if (!data)//通过接口类QThread构造函数知,传入的data为nullptr,执行if分支语句
data = new QThreadData;//新建一个线程时,首先构造一个线程数据类,用来持有线程相关的事件队列、事件分发句柄、
//事件循环列表等整个线程共享的数据
3.QThread是线程管理类,负责新线程的创建、杀死等线程管理工作,他同样是QObject的子类,也具备QObject所共有的线程亲和性,比如在上述示例中,我们是在ui主线程中构造的QThread类,m_thread实例就属于主线程,参与主线程的事件循环,m_thread对象的成员变量(像m_worker这种通过moveToThread()改变了线程亲和性的除外)也属于主线程,在m_thread的成员函数和m_worker的槽函数中访问m_thread的成员变量时,要注意多线程访问临界区的同步问题,通过在我们执行QThread::start()时,会真正调用平台相关的线程api来正式创建一个线程
//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread_win.cpp:432
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;
/*
NOTE: we create the thread in the suspended state, set the
priority and then resume the thread.
since threads are created with normal priority by default, we
could get into a case where a thread (with priority less than
NormalPriority) tries to create a new thread (also with priority
less than NormalPriority), but the newly created thread preempts
its 'parent' and runs at normal priority.
*/
#if defined(Q_CC_MSVC) && !defined(_DLL)
// 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
//此处调用win32 API创建线程,可以看出,线程的真正入口为QThreadPrivate::start(),参数为this指针
d->handle = CreateThread(nullptr, d->stackSize,
reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
#endif
if (!d->handle) {
qErrnoWarning("QThread::start: Failed to create thread");
d->running = false;
d->finished = true;
return;
}
int prio;
d->priority = priority;
switch (d->priority) {
case IdlePriority:
prio = THREAD_PRIORITY_IDLE;
break;
case LowestPriority:
prio = THREAD_PRIORITY_LOWEST;
break;
case LowPriority:
prio = THREAD_PRIORITY_BELOW_NORMAL;
break;
case NormalPriority:
prio = THREAD_PRIORITY_NORMAL;
break;
case HighPriority:
prio = THREAD_PRIORITY_ABOVE_NORMAL;
break;
case HighestPriority:
prio = THREAD_PRIORITY_HIGHEST;
break;
case TimeCriticalPriority:
prio = THREAD_PRIORITY_TIME_CRITICAL;
break;
case InheritPriority:
default:
prio = GetThreadPriority(GetCurrentThread());
break;
}
if (!SetThreadPriority(d->handle, prio)) {
qErrnoWarning("QThread::start: Failed to set thread priority");
}
if (ResumeThread(d->handle) == (DWORD) -1) {
qErrnoWarning("QThread::start: Failed to resume new thread");
}
}
4.让我们的视角切换到线程入口
//E:\Qt\qt-everywhere-src-6.2.1\qtbase\src\corelib\thread\qthread_win.cpp:298
unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
{
QThread *thr = reinterpret_cast<QThread *>(arg);//CreateThread()中,传入的this指针
QThreadData *data = QThreadData::get2(thr);//获取了本线程持有的线程数据
qt_create_tls();
TlsSetValue(qt_current_thread_data_tls_index, data);
data->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
QThread::setTerminationEnabled(false);
{
QMutexLocker locker(&thr->d_func()->mutex);
data->quitNow = thr->d_func()->exited;
}
data->ensureEventDispatcher();//确认是否为该线程分配了事件分发句柄,如果没有,就抓紧分配一个
data->eventDispatcher.loadRelaxed()->startingUp();
#if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC)
// sets the name of the current thread.
// avoid interacting with the binding system while thread is
// not properly running yet
auto priv = QObjectPrivate::get(thr);
QByteArray objectName = (priv->extraData ? priv->extraData->objectName.valueBypassingBindings()
: QString()).toLocal8Bit();
qt_set_thread_name(HANDLE(-1),
objectName.isEmpty() ?
thr->metaObject()->className() : objectName.constData());
#endif
emit thr->started(QThread::QPrivateSignal());//此时发出的started信号,正是我们经常关联的那个
QThread::setTerminationEnabled(true);
thr->run();//调用了run虚函数,里面默认调用了Qt的事件循环,作为该线程的最底层的事件循环,事件系统会单开一篇,这里就不展开了
finish(arg);
return 0;
}