QObject模型和moveToThread

QObject

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

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

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的部分源码:

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的源码:

    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的源码:

    // 将当前线程的事件队列转移到目标线程的事件队列
    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模型

猜你喜欢

转载自blog.csdn.net/yao5hed/article/details/81108514