QObject_thread

QObject::thread

QThread *QObject::thread()

返回对象所在的线程。

QThread *QObject::thread() const
{
    return d_func()->threadData.loadRelaxed()->thread.loadAcquire();
}

void QObject::moveToThread(QThread *targetThread)

将此对象及其孩子关联到targetThread线程,事件处理将在targetThread中继续进行,
在线程关联被改变之前QEvent::ThreadChange事件被发送给这个对象和其孩子对象。

注意:

  1. widget类型(继承QWidget的类)对象只能在主线程中,所以调用moveToThread无效。
  2. 如果对象有父对象,则不能移动该对象。
  3. 调用者必须与此对象在同一线程,除非此对象没有关联线程
  4. 该对象的计时器timer首先在当前线程中停止,然后在targetThread中重新启动(间隔相同)。因此,在线程之间不断移动对象会无限期地延迟计时器事件。
  5. 如果targetThread为nullptr,则此对象及其子对象的所有事件处理都将停止,因为它们不再与任何线程相关联。
使用样例:
int main()
{
    QThread *th = new QThread();
    myobject *obj1= new  myobject("boj1");
    obj1->connect(obj1,&myobject::signal1,obj1,&myobject::slot1);
    obj1->moveToThread(th);//调用在主线程与obj1同一线程
    //因为线程启动后才会执行事件循环,所以必须启动线程
    th->start();
    qDebug()<<"main QThreadid = "<<QThread::currentThreadId()<<",date="<<QDateTime::currentSecsSinceEpoch();
    emit obj1->signal1(111);
    return 0;
}
函数解读:
void QObject::moveToThread(QThread *targetThread)
{
    Q_D(QObject);
    //如果对象有父对象,则不能移动该对象。
    if (d->parent != nullptr) {
        qWarning("QObject::moveToThread: Cannot move objects with a parent");
        return;
    }
    //对象是widget类型的不能移动widget类型的只能在主线程中
    //继承QWidget的类都是widget类型
    //explicit QWidget(QWidget* parent = nullptr, Qt::WindowFlags);
    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;
    //对象所在线程数据,
    QThreadData *thisThreadData = d->threadData.loadRelaxed();
    //如果对象没有关联线程,并且目标线程等于当前线程,可以移动
    if (!thisThreadData->thread.loadAcquire() && currentData == targetData)
     {
        //我们允许将没有线程关联的对象移动到当前线程
        currentData = d->threadData;
    } else if (thisThreadData != currentData) {
        //只能在object所在的线程里面调用此函数
        qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n"
                 "Cannot move to target thread (%p)\n",
                 currentData->thread.loadRelaxed(), thisThreadData->thread.loadRelaxed(), targetData ? targetData->thread.loadRelaxed() : nullptr);
        return;
    }

    // 移动前先发送QEvent::ThreadChange事件
    d->moveToThread_helper();

    if (!targetData)
        targetData = new QThreadData(0);

    //确保在我们移动这个对象时没有人添加/删除连接
    QMutexLocker l(signalSlotLock(this));

    QOrderedMutexLocker locker(&currentData->postEventList.mutex,
                               &targetData->postEventList.mutex);

    // 保持currentData存活(因为我们已经锁定了它)
    currentData->ref();

    // 真正的移动对象
    d_func()->setThreadData_helper(currentData, targetData);

    locker.unlock();

    // now currentData can commit suicide if it wants to
    currentData->deref();
}

void QObjectPrivate::moveToThread_helper()
{
    Q_Q(QObject);
    QEvent e(QEvent::ThreadChange);
    //sendEvent发送ThreadChange事件
    //因为sendEvent是同步调用,所以等对象处理完事件后,才能继续往下走
    QCoreApplication::sendEvent(q, &e);
    //给对象的所有孩子发送ThreadChange事件
    for (int i = 0; i < children.size(); ++i) {
        QObject *child = children.at(i);
        child->d_func()->moveToThread_helper();
    }
}

void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData *targetData)
{
     for (int i = 0; i < currentData->postEventList.size(); ++i) {
        const QPostEvent &pe = currentData->postEventList.at(i);
        if (!pe.event)
            continue;
        if (pe.receiver == q) {
            //将此post事件移动到targetList中
            targetData->postEventList.addEvent(pe);
            const_cast<QPostEvent &>(pe).event = nullptr;
            ++eventsMoved;
        }
    }
    //当前线程在调用moveToThread()后不应该恢复currentSender
    ConnectionData *cd = connections.loadRelaxed();
    if (cd) {
        if (cd->currentSender) {
            cd->currentSender->receiverDeleted();
            cd->currentSender = nullptr;
        }

        // 调整连接中的receiverThreadId值
        if (cd) {
            auto *c = cd->senders;
            while (c) {
                QObject *r = c->receiver.loadRelaxed();
                if (r) {
                    Q_ASSERT(r == q);
                    targetData->ref();
                    QThreadData *old = c->receiverThreadData.loadRelaxed();
                    if (old)
                        old->deref();
                    c->receiverThreadData.storeRelaxed(targetData);
                }
                c = c->next;
            }
        }

    }

    // 设置新线程数据
    targetData->ref();
    threadData.loadRelaxed()->deref();

    //同步其孩子到targetData线程
    //QObject的孩子肯定不是widget一类,所以可以直接移动不用判断
    for (int i = 0; i < children.size(); ++i) {
        QObject *child = children.at(i);
        child->d_func()->setThreadData_helper(currentData, targetData);
    }
}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值