6.QT信号槽的时序分析

前面已经分析了元对象系统、MOC文件和信号槽的连接,本文分析下信号槽的时序

信号的触发通过emit关键字触发,以sigf1为例,通常是这样的

emit sigf1(t1)

emit就是个空宏,在qobjectdefs.h中,没啥用,实质就是个函数调用

#ifndef QT_NO_EMIT
# define emit
#endif

而函数sigf1在MOC中实现

void moctest::sigf1(double _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

定义数组后,调用QMetaObject::activate(在qobject.cpp中)

void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
                           void **argv)
{
    int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m);//计算信号的绝对序号

    if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))//如果为true,表示执行qt的测试代码,执行那个分支无所谓,与qt_signal_spy_callback_set相关的都不重要
        doActivate<true>(sender, signal_index, argv);
    else
        doActivate<false>(sender, signal_index, argv);
}

loadrelaxed调用的其实就是C++11中的atomic类中的load方法,返回的是其中类或结构中包含的数据详细见http://www.cplusplus.com/reference/atomic/atomic/load/

然后调用模板函数doActivate

template <bool callbacks_enabled>
void doActivate(QObject *sender, int signal_index, void **argv)
{
    QObjectPrivate *sp = QObjectPrivate::get(sender);//获取信号的发射对象的QObject数据

    if (sp->blockSig)//如果信号阻塞标志位为true,不调用槽函数
        return;

    Q_TRACE_SCOPE(QMetaObject_activate, sender, signal_index);

    if (sp->isDeclarativeSignalConnected(signal_index)//qml相关,暂时不用管
            && QAbstractDeclarativeData::signalEmitted) {
        Q_TRACE_SCOPE(QMetaObject_activate_declarative_signal, sender, signal_index);
        QAbstractDeclarativeData::signalEmitted(sp->declarativeData, sender,
                                                signal_index, argv);
    }

    const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.loadAcquire() : nullptr;

    void *empty_argv[] = { nullptr };
    if (!argv)
        argv = empty_argv;

    if (!sp->maybeSignalConnected(signal_index)) {//可能是声明性关联,如果是,啥也不做,没太懂,但是问题不大
        // The possible declarative connection is done, and nothing else is connected
        if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr)
            signal_spy_set->signal_begin_callback(sender, signal_index, argv);
        if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
            signal_spy_set->signal_end_callback(sender, signal_index);
        return;
    }

    if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr)//不执行
        signal_spy_set->signal_begin_callback(sender, signal_index, argv);

    bool senderDeleted = false;
    {
    Q_ASSERT(sp->connections.loadAcquire());
    QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed());//发送对象的所有连接信息
    QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed();//获取指向信号connection list的指针

    const QObjectPrivate::ConnectionList *list;
    if (signal_index < signalVector->count())
        list = &signalVector->at(signal_index);//根据绝对序号获取对应信号的ConnectionList
    else
        list = &signalVector->at(-1);//如果信号的绝对序号不合法,获取第一个信号的ConnectionList

    Qt::HANDLE currentThreadId = QThread::currentThreadId();//获取当前程序的线程ID
    bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.loadRelaxed();//判断发送端和当前程序的是否在同一线程

    // We need to check against the highest connection id to ensure that signals added
    // during the signal emission are not emitted in this emission.
    uint highestConnectionId = connections->currentConnectionId.loadRelaxed();
    do {
        QObjectPrivate::Connection *c = list->first.loadRelaxed();//找到connectionlist中的第一个connection
        if (!c)
            continue;

        do {
            QObject * const receiver = c->receiver.loadRelaxed();//找到connection中的接收信号的对象
            if (!receiver)
                continue;

            QThreadData *td = c->receiverThreadData.loadRelaxed();//接收信号对象程序所在的线程数据
            if (!td)
                continue;

            bool receiverInSameThread;
            if (inSenderThread) {//多线程处理
                receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
            } else {
                // need to lock before reading the threadId, because moveToThread() could interfere
                QMutexLocker lock(signalSlotLock(receiver));
                receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
            }


            // determine if this connection should be sent immediately or
            // put into the event queue
            if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
                || (c->connectionType == Qt::QueuedConnection)) {//接收端和发送端在多线程的情况下,调用queued_activate
                queued_activate(sender, signal_index, c, argv);
                continue;
#if QT_CONFIG(thread)
            } else if (c->connectionType == Qt::BlockingQueuedConnection) {//BlockingQueuedConnection的处理情况
                if (receiverInSameThread) {
                    qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
                    "Sender is %s(%p), receiver is %s(%p)",
                    sender->metaObject()->className(), sender,
                    receiver->metaObject()->className(), receiver);
                }
                QSemaphore semaphore;
                {
                    QBasicMutexLocker locker(signalSlotLock(sender));
                    if (!c->receiver.loadAcquire())
                        continue;
                    QMetaCallEvent *ev = c->isSlotObject ?
                        new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore) :
                        new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction,
                                           sender, signal_index, argv, &semaphore);
                    QCoreApplication::postEvent(receiver, ev);
                }
                semaphore.acquire();
                continue;
#endif
            }

            QObjectPrivate::Sender senderData(receiverInSameThread ? receiver : nullptr, sender, signal_index);//根据发送端和接收端是否在同一线程对senderData进行初始化

            if (c->isSlotObject) {//如果是Qt5中的新式语法,槽函数的对象不为空
                c->slotObj->ref();

                struct Deleter {
                    void operator()(QtPrivate::QSlotObjectBase *slot) const {
                        if (slot) slot->destroyIfLastRef();
                    }
                };//自定义智能指针unique_ptr的删除器
                const std::unique_ptr<QtPrivate::QSlotObjectBase, Deleter> obj{c->slotObj};

                {
                    Q_TRACE_SCOPE(QMetaObject_activate_slot_functor, obj.get());
                    obj->call(receiver, argv);//直接调用槽函数
                }
            } else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {//如果是旧式语法,使用信号槽的宏,那么需要调用callFunction,
                //we compare the vtable to make sure we are not in the destructor of the object.
                const int method_relative = c->method_relative;//获得相对序号
                const auto callFunction = c->callFunction;
                const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0;
                if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr)
                    signal_spy_set->slot_begin_callback(receiver, methodIndex, argv);

                {
                    Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, methodIndex);
                    callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv);//callFunction就是MOC文件中的qt_static_metacall
                }

                if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
                    signal_spy_set->slot_end_callback(receiver, methodIndex);
            } else {//还有一种旧式语法,没有用到,先不用管,问题不大
                const int method = c->method_relative + c->method_offset;

                if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) {
                    signal_spy_set->slot_begin_callback(receiver, method, argv);
                }

                {
                    Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, method);
                    QMetaObject::metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv);
                }

                if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
                    signal_spy_set->slot_end_callback(receiver, method);
            }
        } while ((c = c->nextConnectionList.loadRelaxed()) != nullptr && c->id <= highestConnectionId);//内层循环,遍历对应信号的连接连接列表

    } while (list != &signalVector->at(-1) &&
        //start over for all signals;
        ((list = &signalVector->at(-1)), true));//外层循环,遍历每个信号的connectionlist

        if (connections->currentConnectionId.loadRelaxed() == 0)
            senderDeleted = true;
    }
    if (!senderDeleted) {
        sp->connections.loadRelaxed()->cleanOrphanedConnections(sender);

        if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
            signal_spy_set->signal_end_callback(sender, signal_index);
    }
}

上面的代码看着很多,先不管多线程的情况,只考虑直连的情况,开始调用信号函数,然后调用activate,之后再调用doactivate,接着就是根据信号的绝对序号找到信号的connectionlist,遍历该connectionlist中的每个connection(connection通过connect函数得到并添加++),然后看每个connection中是否有函数指针qt_static_metacall或者有函数指针所在的类对象,然后依次调用qt_static_metacall或者直接调用成员函数

再次引用https://qtguide.ustclug.org/中的图就是这样的

 

参考

Qt5.14源码

https://qtguide.ustclug.org/

 

欢迎大家评论交流,作者水平有限,如有错误,欢迎指出

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值