QThread有两种使用方式:
方式1:继承QThread,重写run函数,调用QThread::start(),就会创建新线程,run为入口函数。
方式2:继承QObject,使用QObject::moveToThread,配合信号/槽,在新线程中执行槽函数。
查看源码(win平台下),大概流程如下:
<QThread_win.cpp>
void QThread::start(Priority priority)
{
d->handle = CreateThread(NULL, 0, QThreadPrivate::start, this, 0, NULL);
}
<QThread_win.cpp>
QThreadPrivate::start()
{
emit thr->started(QThread::QPrivateSignal());
thr->run();
}
在 QThread_win.cpp中,start函数创建了线程,并最终调用到QThread::run()
此时如果是继承了QThread,并重写虚函数run,则会执行自定义的QThread子类的run函数,线程使用到此结束。
如果没有重写QThread的run函数,而是使用moveToThread方式,将会执行以下流程:
<QThread.cpp>
void QThread::run()
{
(void) exec();
}
void QThread::exec()
{
QEventLoop eventLoop;
int returnCode = eventLoop.exec();
return returnCode;
}
在QThread的线程函数中,创建并执行了事件循环QEventLoop。
<QEventLoop.cpp>
int QEventLoop::exec(ProcessEventsFlags flags)
{
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
if (!d->threadData->hasEventDispatcher())
return false;
return d->threadData->eventDispatcher.load()->processEvents(flags);
}
在QEventLoop::processEvents里可以发现,线程在循环遍历 d->threadData->eventDispatcher中的事件队列。
而moveToThread其实就是把QObject中的d->threadData放到新线程中,自然而然的d->threadData中的事件队列也就得以在新线程中被遍历,事件对应的处理函数就在新线程中执行了。
附moveToThread源码:
void QObject::moveToThread(QThread *targetThread)
{
Q_D(QObject);
if (d->threadData->thread == targetThread) {
// object is already in this thread
return;
}
if (d->parent != 0) {
qWarning("QObject::moveToThread: Cannot move objects with a parent");
return;
}
if (d->isWidget) {
qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread");
return;
}
QThreadData *currentData = QThreadData::current();
QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr;
if (d->threadData->thread == 0 && currentData == targetData) {
// one exception to the rule: we allow moving objects with no thread affinity to the current thread
currentData = d->threadData;
} else if (d->threadData != currentData) {
qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n"
"Cannot move to target thread (%p)\n",
currentData->thread.load(), d->threadData->thread.load(), targetData ? targetData->thread.load() : nullptr);
#ifdef Q_OS_MAC
qWarning("You might be loading two sets of Qt binaries into the same process. "
"Check that all plugins are compiled against the right Qt binaries. Export "
"DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.");
#endif
return;
}
// prepare to move
d->moveToThread_helper();
if (!targetData)
targetData = new QThreadData(0);
QOrderedMutexLocker locker(¤tData->postEventList.mutex,
&targetData->postEventList.mutex);
// keep currentData alive (since we've got it locked)
currentData->ref();
// move the object
d_func()->setThreadData_helper(currentData, targetData);
locker.unlock();
// now currentData can commit suicide if it wants to
currentData->deref();
}