QObject模型和moveToThread

QObject

  每个线程都以QThread实例表示,并且在内部拥有一个QThreadData来表示线程的基本数据。
事件处理是建立在线程上进行的,每个线程拥有一个待处理事件列表postEventList,保存了待处理的事件QPostEvent(如鼠标、键盘以及用户事件),同时每个线程维护了一个QEventLoop栈,但只有栈顶的QEventLoop对象参与当前事件处理

  包括QThread对象在内,每个QObject对象都可以属于一个线程,但QObject与其父对象须属于同一线程,如某对象有父对象或是widget,则不能更换线程。QThreadData是该对象所属的线程对象,在QObject创建时在其构造函数中被设置。
QThread的Private类是QThreadPrivate,里面包含成员QThreadData *data。这个QThreadData包含了这个线程的一些重要数据,比如下面的几个

1
2
3
4
5
QStack<QEventLoop *> eventLoops;
QPostEventList postEventList; // 当前线程的待处理事件列表
QThread *thread; // 当前线程的线程对象
Qt:: HANDLE threadId; // 实际的线程句柄
QAtomicPointer<QAbstractEventDispatcher > eventDispatcher; // 事件分发器,负责读取和处理数据

  一个线程的事件循环为驻足在该线程中的所有QObjects派发了所有事件,其中包括在这个线程中创建的所有对象,或是移植到这个线程中的对象。一个QObject的线程关联性(thread affinity)是指该对象驻足(live in)在某个线程内。在任何时间都可以通过调用QObject::thread()来查询线程关联性,它适用于在QThread对象构造函数中构建的对象。 QObject对象的事件处理函数始终要在其所关联线程的上下文中执行。

moveToThread概述

函数对QObject子类的对象有以下要求:

  • parent非0的对象不能被移动!
  • QWidget及其派生类对象不能被移动!
  • 该函数必须在对象关联的线程内调用!

moveToThread()有三大任务:

  1. moveToThread_helper函数:生成并通过sendEvent()派发 QEvent::ThreadChange事件,在QObject::event中处理
  2. setThreadData_helper函数:将该对象在当前事件队列中的事件移动到目标线程的事件队列中
  3. 解除在当前线程中的timer注册,在目标线程中重新注册

函数可以改变一个QObject的依附性;它将改变这个对象以及它的孩子们的依附性。因为QObject不是线程安全的,我们必须在对象所驻足的线程中使用此函数;也就是说,你只能将对象从它所驻足的线程中推送到其他线程中,而不能从其他线程中拉回来。

moveToThread源码分析

moveToThread的部分源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void QObject::moveToThread(QThread *targetThread)
{
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;
}
......
else if (d->threadData != currentData) {
qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p)./n"
}
//省略,获得当前线程和目标线程的数据
//对当前对象和子对象调用sendEvent派发QEvent::ThreadChange,
d->moveToThread_helper();
//将当前线程的事件队列转移到目标线程的事件队列
d_func()->setThreadData_helper(currentData, targetData);

前面三个if确定了函数的三条使用要求。

moveToThread_helper的源码:

1
2
3
4
5
6
7
Q_Q(QObject);
QEvent e(QEvent::ThreadChange);
QCoreApplication::sendEvent(q, &e);
for (int i = 0; i < children.size(); ++i) {
QObject *child = children.at(i);
child->d_func()->moveToThread_helper();
}

setThreadData_helper的源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 将当前线程的事件队列转移到目标线程的事件队列
int eventsMoved = 0;
for (int i = 0; i < currentData->postEventList.size(); ++i) {
const QPostEvent &pe = currentData->postEventList.at(i);
if (!pe.event)
continue;
if (pe.receiver == q) {
// move this post event to the targetList
targetData->postEventList.addEvent(pe);
const_cast<QPostEvent &>(pe).event = 0;
++eventsMoved;
}
}
if (eventsMoved > 0 && targetData->eventDispatcher.load()) {
targetData->canWait = false;
targetData->eventDispatcher.load()->wakeUp();
}
. . . . . .
for (int i = 0; i < children.size(); ++i) {
QObject *child = children.at(i);
child->d_func()->setThreadData_helper(currentData, targetData);
}

参考:QObject模型