QT中的信号-槽函数与多线程

项目中遇到了关于多线程编程问题

Class obj中有一个QTimer。线程A和线程B都有机会重启这个QTimer。

 然而,在QT文档中QTimer并不是一个线程安全的类,因此就出现了怎样保证跨线程调用的安全性


首先,obj是是有线程概念的,它必须属于一个特定线程的(当然也可以使用moveToThread将它放入另外一个线程中,所有继承自QObject都有一个threadid的属性,用来标明此对象所属线程)。


为了能够跨线程调用,一种方法是使用类似线程锁进行保护,另外一种方法使用QT的信号槽机制。


信号槽机制网上的内容很多,尤其是对于QObject::connect函数的第五个参数的说明。这个参数可以有好几个选项,最终的结果是发出的信号应该到底是直接处理(DirectConnection)还是转变成消息进入QEventLoop中进行处理

当直接处理时,emit 信号等同于直接调用槽函数(例如使用DirectConnection或者使用AutoConnection时,发送者和接收者在同一个线程中。使用DirectConnection需要注意,因为这会导致跨线程调用问题)。  

不直接处理时,emit 信号会生成一个QEvent::MetaCall的event,将此事件放入到槽函数所属对象所在的线程中的QEventLoop中,这样,线程就有机会从中取到MetaCall事件,然后通过QObject::event实现事件的解释,最终定位到槽函数中。


参考文章:http://woboq.com/blog/how-qt-signals-slots-work.html

class TestObject : public QObject
{
    Q_OBJECT
public:
    explicit TestObject(QObject *parent = 0):QObject(parent),mTimer(this){
        connect(&mTimer,SIGNAL(timeout()),this,SLOT(Timeout()));
        mTimer.start(1000);
     }
public slots:
    void Timeout(){
        printf("timeout\n");
    }
private:
    QTimer mTimer;
};
直接构造后,每1s输出1个打印。

信号的连接:

connect(&mTimer,SIGNAL(timeout()),this,SLOT(Timeout()));

SIGNAL和SLOT都是宏定义,在qobjectdefs.h文件中定义,如下:

#ifndef QT_NO_DEBUG
# define QLOCATION "\0" __FILE__ ":" QTOSTRING(__LINE__)
# ifndef QT_NO_KEYWORDS
#  define METHOD(a)   qFlagLocation("0"#a QLOCATION)
# endif
# define SLOT(a)     qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)
#else
# ifndef QT_NO_KEYWORDS
#  define METHOD(a)   "0"#a
# endif
# define SLOT(a)     "1"#a
# define SIGNAL(a)   "2"#a
#endif
上述调试版本增加了文件名和所在行信息

非调试版本只是添加了"1"或者"2"的前缀信息。

connect(&Timer,SIGNAL(timeout()),this,SLOT(Timeout()))最后被扩展为:

connect(&Timer,"1timeout()",this,"2Timeout()",Qt::AutoConnection)  ,这个函数的定义如下,注释在代码中:

bool QObject::connect(const QObject *sender, const char *signal,
                      const QObject *receiver, const char *method,
                      Qt::ConnectionType type)
{
    {
        const void *cbdata[] = { sender, signal, receiver, method, &type };
        if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) //connect callback, 没有看到哪个地方在用,先略过
            return true;
    }

#ifndef QT_NO_DEBUG
    bool warnCompat = true;
#endif
    if (type == Qt::AutoCompatConnection) {
        type = Qt::AutoConnection;
#ifndef QT_NO_DEBUG
        warnCompat = false;
#endif
    }

    if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { 
        qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
                 sender ? sender->metaObject()->className() : "(null)",
                 (signal && *signal) ? signal+1 : "(null)",
                 receiver ? receiver->metaObject()->className() : "(null)",
                 (method && *method) ? method+1 : "(null)");
        return false;
    }
    QByteArray tmp_signal_name;

    if (!check_signal_macro(sender, signal, "connect", "bind"))  //判断信号是否1开头
        return false;
    const QMetaObject *smeta = sender->metaObject();//Meta Object 系统
    const char *signal_arg = signal;
    ++signal; //skip code
    int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); //从Meta Object系统中获取signal信息,局部索引
    if (signal_index < 0) {
        //没找到,试一试将发送者的信号参数中的空白行去掉,将const放在最前面,将const引用类型换成值类型。(当然肯定不是这么简单,例如char* const *就不能变成const char**)
        // check for normalized signatures
        tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
        signal = tmp_signal_name.constData() + 1;

        smeta = sender->metaObject();
        signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
    }
    if (signal_index < 0) {
        // re-use tmp_signal_name and signal from above
        //还没找到,试一试将MetaObject中存储的信号参数中的空白行去掉,将const放在最前面,将const引用类型换成值类型。
        smeta = sender->metaObject();
        signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true);
    }
    //例如:信号声明为 void MySignal(QString)。 对应的Signature为: "2MySignal(QString)"
    //连接时: connect(this,SIGNAL(MySignal(const QString&))....)。对应的Signature为: "2MySignal(const QString&)", 字符串肯定不匹配。不过Normalize后得到"1MySignal(QString)"就能匹配了。

    if (signal_index < 0) {//没有找到signal
        err_method_notfound(sender, signal_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return false;
    }
    signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
    int signalOffset, methodOffset;
    computeOffsets(smeta, &signalOffset, &methodOffset);
    int signal_absolute_index = signal_index + methodOffset;
    signal_index += signalOffset;

    QByteArray tmp_method_name;
    int membcode = extract_code(method);

    if (!check_method_code(membcode, receiver, method, "connect"))//检查接收者(可能是slot,也可能是signal)
        return false;
    const char *method_arg = method;
    ++method; // skip code

    const QMetaObject *rmeta = receiver->metaObject();
    int method_index_relative = -1;
    switch (membcode) {
    case QSLOT_CODE:
        method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
        break;
    case QSIGNAL_CODE:
        method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
        break;
    }

    if (method_index_relative < 0) {
        //和信号一样,可能会对参数做一些修改,然后看看能不能找到Method
        // check for normalized methods
        tmp_method_name = QMetaObject::normalizedSignature(method);
        method = tmp_method_name.constData();

        // rmeta may have been modified above
        rmeta = receiver->metaObject();
        switch (membcode) {
        case QSLOT_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
            if (method_index_relative < 0)
                method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true);
            break;
        case QSIGNAL_CODE:
            method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
            if (method_index_relative < 0)
                method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true);
            break;
        }
    }

    if (method_index_relative < 0) { //还没找到Receive的Method,只能报错了。
        err_method_notfound(receiver, method_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return false;
    }

    if (!QMetaObject::checkConnectArgs(signal, method)) { //检查信号和槽函数的参数是否匹配:参数一致,或者接收者参数多于发送者参数(接收者参数有默认值)
        qWarning("QObject::connect: Incompatible sender/receiver arguments"
                 "\n        %s::%s --> %s::%s",
                 sender->metaObject()->className(), signal,
                 receiver->metaObject()->className(), method);
        return false;
    }

    int *types = 0;
    //跨线程发送信号,参数类型必须能够被QtMetaObject识别(若不能识别,最简单的方法是发送这个类型的指针)
    if ((type == Qt::QueuedConnection)
            && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))
        return false;

#ifndef QT_NO_DEBUG
    if (warnCompat) {
        QMetaMethod smethod = smeta->method(signal_absolute_index);
        QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
        check_and_warn_compat(smeta, smethod, rmeta, rmethod);
    }
#endif
    //正真的注册发生在这个函数中
    //事实上,发送者这个QObject拥有一个QObjectConnectionListVector类型的列表,其中每项的数据为:QObjectPrivate::Connection类型的链表。
    //这是一个链表数组
    if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types))
        return false;

    const_cast<QObject*>(sender)->connectNotify(signal - 1);
    return true;
}

前面只是做了检查工作,关键的工作发生在QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types)。

bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
                                 const QObject *receiver, int method_index,
                                 const QMetaObject *rmeta, int type, int *types)
{
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    int method_offset = rmeta ? rmeta->methodOffset() : 0;
    QObjectPrivate::StaticMetaCallFunction callFunction =
        (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata)
        ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0;

    QOrderedMutexLocker locker(signalSlotLock(sender),
                               signalSlotLock(receiver));

    if (type & Qt::UniqueConnection) {
        QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
        if (connectionLists && connectionLists->count() > signal_index) {  //已经注册信号了,增加新的接收者(支持1个信号,多个接收者)
            const QObjectPrivate::Connection *c2 =
                (*connectionLists)[signal_index].first;

            int method_index_absolute = method_index + method_offset;

            while (c2) {
                if (c2->receiver == receiver && c2->method() == method_index_absolute) //重复注册
                    return false;
                c2 = c2->nextConnectionList;
            }
        }
        type &= Qt::UniqueConnection - 1;
    }

    QObjectPrivate::Connection *c = new QObjectPrivate::Connection;
    c->sender = s;
    c->receiver = r;
    c->method_relative = method_index;
    c->method_offset = method_offset;
    c->connectionType = type;
    c->argumentTypes = types;
    c->nextConnectionList = 0;
    c->callFunction = callFunction;

    QT_TRY {
        QObjectPrivate::get(s)->addConnection(signal_index, c); //添加到向量表中。
    } QT_CATCH(...) {
        delete c;
        QT_RETHROW;
    }

    c->prev = &(QObjectPrivate::get(r)->senders);
    c->next = *c->prev;
    *c->prev = c;
    if (c->next)
        c->next->prev = &c->next;

    QObjectPrivate *const sender_d = QObjectPrivate::get(s);
    if (signal_index < 0) {
        sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;
    } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {
        sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
    }

    return true;
}


信号的发送:

emit实际上是一个空的宏定义: #define emit
例如:emit test2("123")实际上是 test2("123")
Mysignal的定义在Moc文件中可以看到。

void TestObject::test2(QString _t1)
{
    void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

上面可以看到,参数个数包括返回值(返回值用0表示)。
QString被转换成了void*类型了。若是发送者和接收者在同一个线程里,这样不会有问题。 若是不在同一个线程里,接收者拿到的指针会不会失效呢?(在后面可以看到,实际上,跨线程发送信号会导致参数备份)

void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
                           void **argv)
{
    int signalOffset;
    int methodOffset;
    computeOffsets(m, &signalOffset, &methodOffset);   //老规矩,先得到索引位置

    int signal_index = signalOffset + local_signal_index;
    //不好意思,这个信号没有人connect
    if (!sender->d_func()->isSignalConnected(signal_index))
        return; // nothing connected to these signals, and no spy

    //不好意思,这个Object被设置成阻塞(不能发送任何信号了)
    //bool QObject::blockSignals ( bool block )
    if (sender->d_func()->blockSig)
        return;

    int signal_absolute_index = methodOffset + local_signal_index;

    void *empty_argv[] = { 0 };
    if (qt_signal_spy_callback_set.signal_begin_callback != 0) { //QTest框架用的东西,略过(QSignalDumper)
        qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index,
                                                         argv ? argv : empty_argv);
    }

    Qt::HANDLE currentThreadId = QThread::currentThreadId();

    QMutexLocker locker(signalSlotLock(sender));
    //发送者的连接向量
    QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
    if (!connectionLists) {
        locker.unlock();
        if (qt_signal_spy_callback_set.signal_end_callback != 0)
            qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);
        return;
    }
    //标记这个连接向量表正在使用
    ++connectionLists->inUse;


    //得到向量表中的链表
    const QObjectPrivate::ConnectionList *list;
    if (signal_index < connectionLists->count())
        list = &connectionLists->at(signal_index);
    else
        list = &connectionLists->allsignals;

    do {
        //遍历链表
        QObjectPrivate::Connection *c = list->first;
        if (!c) continue;
        // We need to check against last here to ensure that signals added
        // during the signal emission are not emitted in this emission.
        QObjectPrivate::Connection *last = list->last;

        do {
            if (!c->receiver) //接收者为空
                continue;

            QObject * const receiver = c->receiver;
            const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;

            // determine if this connection should be sent immediately or
            // put into the event queue
            if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
                || (c->connectionType == Qt::QueuedConnection)) {  //异步,直接使用事件来处理吧
                //queue_activate需要看仔细咯
                queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);
                continue;
#ifndef QT_NO_THREAD
            } else if (c->connectionType == Qt::BlockingQueuedConnection) {//BlockingQueuedConnection需要单独处理
                locker.unlock();
                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;
                QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,
                                                                         c->callFunction,
                                                                         sender, signal_absolute_index,
                                                                         0, 0,
                                                                         argv ? argv : empty_argv,
                                                                         &semaphore));
               
                semaphore.acquire(); //阻塞自己,等待接收者调用semaphore.release
                locker.relock();
                continue;
#endif
            }
            //下面是处理同一线程情况
            QObjectPrivate::Sender currentSender;
            QObjectPrivate::Sender *previousSender = 0;
            /*     Sender数据结构
                   struct Sender
		    {
        		QObject *sender;
		        int signal;
		        int ref;
		    };
             
            */
            if (receiverInSameThread) {
                currentSender.sender = sender;
                currentSender.signal = signal_absolute_index;
                currentSender.ref = 1;
                previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender);
            }
            //QMetaObject的callFunction
            //typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
            const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;

            //寻找接收者Method的索引位置
            const int method_relative = c->method_relative;

/*
moc文件中MetaObject信息如下:
const QMetaObject TestObject::staticMetaObject = {
    { &QObject::staticMetaObject, qt_meta_stringdata_TestObject,
      qt_meta_data_TestObject, &staticMetaObjectExtraData }
};
通过metaObject接口那大QMetaObject数据,其中methodOffset()得到method索引(从父类算起的绝对索引)
const QMetaObject *TestObject::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
}

*/
            if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
                //we compare the vtable to make sure we are not in the destructor of the object.
                locker.unlock();
                if (qt_signal_spy_callback_set.slot_begin_callback != 0)
                    qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv);
/*
callFunction是一个函数指针,定义在我们的moc文件中
void TestObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        Q_ASSERT(staticMetaObject.cast(_o));
        TestObject *_t = static_cast<TestObject *>(_o);
        switch (_id) {
        case 0: _t->test2(); break;
        case 1: _t->Timeout(); break;
        case 2: _t->testSlot(); break;
        default: ;
        }
    }
    Q_UNUSED(_a);
}
*/
                callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, c->method());
                locker.relock();
            } else {
                //什么情况下会进入这个分支??
                //
                const int method = method_relative + c->method_offset;
                locker.unlock();

                if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
                    qt_signal_spy_callback_set.slot_begin_callback(receiver,
                                                                method,
                                                                argv ? argv : empty_argv);
                }

#if defined(QT_NO_EXCEPTIONS)
                metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
#else
                QT_TRY {
/*
int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
{
    if (QMetaObject *mo = object->d_ptr->metaObject)
        return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv);
    else
        return object->qt_metacall(cl, idx, argv);
}
//qt_metacall也在moc文件中定义
*/
                    metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
                } QT_CATCH(...) {
                    locker.relock();
                    if (receiverInSameThread)
                        QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);

                    --connectionLists->inUse;
                    Q_ASSERT(connectionLists->inUse >= 0);
                    if (connectionLists->orphaned && !connectionLists->inUse)
                        delete connectionLists;
                    QT_RETHROW;
                }
#endif

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, method);

                locker.relock();
            }
//
            if (receiverInSameThread)
                QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);

            if (connectionLists->orphaned)
                break;
        } while (c != last && (c = c->nextConnectionList) != 0);

        if (connectionLists->orphaned)
            break;
    } while (list != &connectionLists->allsignals &&
        //start over for all signals;
        ((list = &connectionLists->allsignals), true));

    --connectionLists->inUse;
    Q_ASSERT(connectionLists->inUse >= 0);
    //对向量表做一些维护工作
    if (connectionLists->orphaned) {
        if (!connectionLists->inUse)
            delete connectionLists;
    } else if (connectionLists->dirty) {
        sender->d_func()->cleanConnectionLists();
    }

    locker.unlock();

    if (qt_signal_spy_callback_set.signal_end_callback != 0)
        qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index);

}

其中的queued_activate函数定义:

static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
    if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) {
        QMetaMethod m = sender->metaObject()->method(signal);
        int *tmp = queuedConnectionTypes(m.parameterTypes());
        if (!tmp) // cannot queue arguments
            tmp = &DIRECT_CONNECTION_ONLY;
        if (!c->argumentTypes.testAndSetOrdered(0, tmp)) {
            if (tmp != &DIRECT_CONNECTION_ONLY)
                delete [] tmp;
        }
    }
    //参数有问题。。
    if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
        return;
    //得到参数个数
    int nargs = 1; // include return type
    while (c->argumentTypes[nargs-1])
        ++nargs;
    int *types = (int *) qMalloc(nargs*sizeof(int));
    Q_CHECK_PTR(types);
    void **args = (void **) qMalloc(nargs*sizeof(void *));
    Q_CHECK_PTR(args);
    types[0] = 0; // return type
    args[0] = 0; // return value
    //在这里,将信号中的参数类型和数据存储到args和types中,可以认为是做了一个备份。。
    /*
        void * QMetaType::construct ( int type, const void * copy = 0 ) [static]
        Returns a copy of copy, assuming it is of type type. If copy is zero, creates a default type.
        type的类型包括常用的int,float等基本类型,也包括QDate,QDateTime,QString,QByteArray等QT类型,也可以是用户注册的一些类型。
        不管如何,都会将信号传递过来的参数进行拷贝。例如:当信号中的参数为QByteArray时:args[n]=new NS(QByteArray)(*static_cast<const NS(QByteArray)*>(copy));
    */
    for (int n = 1; n < nargs; ++n)
        args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]);
    //发送QMetaCallEvent
    QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset,
                                                                c->method_relative,
                                                                c->callFunction,
                                                                sender, signal, nargs,
                                                                types, args));
}

注意其中的QMetaCallEvent。


QTimer触发事件过程

首先需要注册

int QObject::startTimer(int interval)
{
    Q_D(QObject);

    if (interval < 0) {
        qWarning("QObject::startTimer: QTimer cannot have a negative interval");
        return 0;
    }

    d->pendTimer = true;                                // set timer flag

    if (!d->threadData->eventDispatcher) {
        qWarning("QObject::startTimer: QTimer can only be used with threads started with QThread");
        return 0;
    }
    return d->threadData->eventDispatcher->registerTimer(interval, this);
}

void QEventDispatcherUNIX::registerTimer(int timerId, int interval, QObject *obj)
{
#ifndef QT_NO_DEBUG
    if (timerId < 1 || interval < 0 || !obj) {
        qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments");
        return;
    } else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
        qWarning("QObject::startTimer: timers cannot be started from another thread");
        return;
    }
#endif

    Q_D(QEventDispatcherUNIX);
    d->timerList.registerTimer(timerId, interval, obj);
}
也就是说,这个定时器被保存到timerList中了。而timerList在QEventDispatcher中管理,而这个时间分发器又是QThreadData的成员。QThreadData代表着一个线程。

发过来说,一个线程中所有的QObject拥有同一份QThreadData,通过这个成员可以得到QEventDispatcher。每一个线程中其实都有至少一个Event Queue的,QeventDispatcher实现对event queue的管理

定时事件如何判定

bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherUNIX);
    d->interrupt = false;

    // we are awake, broadcast it
    emit awake();
    //立刻分发所有posted事件
    QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);

    int nevents = 0;
    const bool canWait = (d->threadData->canWait
                          && !d->interrupt
                          && (flags & QEventLoop::WaitForMoreEvents));

    if (canWait)
        emit aboutToBlock();

    if (!d->interrupt) {
        // return the maximum time we can wait for an event.
        timeval *tm = 0;
        timeval wait_tm = { 0l, 0l };
        if (!(flags & QEventLoop::X11ExcludeTimers)) {
            if (d->timerList.timerWait(wait_tm))
                tm = &wait_tm;
        }

        if (!canWait) {
            if (!tm)
                tm = &wait_tm;

            // no time to wait
            tm->tv_sec  = 0l;
            tm->tv_usec = 0l;
        }

        nevents = d->doSelect(flags, tm); //处理QSocketNotifier事件

        // activate timers
        if (! (flags & QEventLoop::X11ExcludeTimers)) {
            nevents += activateTimers();      //处理定时器
        }
    }
    // return true if we handled events, false otherwise
    return (nevents > 0);
}

int QTimerInfoList::activateTimers()
{
    if (qt_disable_lowpriority_timers || isEmpty())
        return 0; // nothing to do

    int n_act = 0, maxCount = 0;
    firstTimerInfo = 0;
    //当前时间
    timeval currentTime = updateCurrentTime();
    //
    repairTimersIfNeeded();

    //QTimerInfoList是有序的
    // Find out how many timer have expired
    for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
        if (currentTime < (*it)->timeout)
            break;
        maxCount++;
    }

    //fire the timers.
    while (maxCount--) {
        if (isEmpty())
            break;

        QTimerInfo *currentTimerInfo = first();
        if (currentTime < currentTimerInfo->timeout)
            break; // no timer has expired

        if (!firstTimerInfo) {
            firstTimerInfo = currentTimerInfo;
        } else if (firstTimerInfo == currentTimerInfo) {
            // avoid sending the same timer multiple times
            break;
        } else if (currentTimerInfo->interval <  firstTimerInfo->interval
                   || currentTimerInfo->interval == firstTimerInfo->interval) {
            firstTimerInfo = currentTimerInfo;
        }

        // remove from list
        removeFirst();
        
        
        // determine next timeout time
        currentTimerInfo->timeout += currentTimerInfo->interval;
        if (currentTimerInfo->timeout < currentTime)
            currentTimerInfo->timeout = currentTime + currentTimerInfo->interval;

        // reinsert timer
        timerInsert(currentTimerInfo);
        if (currentTimerInfo->interval.tv_usec > 0 || currentTimerInfo->interval.tv_sec > 0)
            n_act++;

        if (!currentTimerInfo->activateRef) {
            // send event, but don't allow it to recurse
            currentTimerInfo->activateRef = ¤tTimerInfo;
            //这里生成QTimerEvent,发送给QTimer,在QTimer::timerEvent中进行处理
            QTimerEvent e(currentTimerInfo->id);
            QCoreApplication::sendEvent(currentTimerInfo->obj, &e);

            if (currentTimerInfo)
                currentTimerInfo->activateRef = 0;
        }
    }

    firstTimerInfo = 0;
    return n_act;
}

QEventDispatcher会处理event queue中时间(好像只看到QSocketNotifier和QTimer),

需要注意:发送事件使用的是sendEvent,这意味着,当前线程会直接调用currentTimerinfo->obj对应的定时器事件的处理函数。为了保证一致性,需要currentTimerInfo->obj关联的是当前线程。
当timeout时,生成的QTimerEvent会调用到QTimer::timerEvent接口。

定时器响应:

void QTimer::timerEvent(QTimerEvent *e)
{
    if (e->timerId() == id) {
        if (single) //single shot定时器
            stop();
        emit timeout();
    }
}
看到emit timeout(),又回到了信号与槽。

再看看QMetaCallEvent,它其实是一个类型为QEvent::MetaCall的事件。

跨线程信号槽调用时,拥有槽函数(信号)的对象所在的线程在处理事件时,发现了QMetaCallEvent,通过解析,能够从里面拿到接收者信息,以及其他接收者槽函数或者信号,再通过MetaObject的callFunction实现调用

QTimer::SingleShoot

当延时为0时,会通过QMetaObject直接调用接收者的槽函数或者信号,
当延时大于0时,和普通的QTimer一样,先将这个定时器进行注册,然后再等待触发。


另外我的有问题的代码如下:

class Object:public QObject{
Q_OBJECT
public: 
    Object(){
       mTimer.setSingleShot(true);
       connect(&mTimer,SIGNAL(timeout()),this,SLOT(timeout()));
       mTimer.start(1000);
    }
private slots:
    void timeout(){
       mTimer.start(1000);
    }
private:
    QTimer mTimer;
}
Object *A=new Object();
moveToThread(new QThread());


A关联到线程A中,不过mTimer还保留在原来的线程中(假设为B)
mTimer第一次start的时候,mTimer被注册到线程B的eventDispatcher中。当timeout发生时,mTimer第二次调用start的时候,mTimer关联线程B,却在线程A的槽函数中被访问。并且在start的时候会访问线程B的QEventDispatcher。这些核心数据有些并不是线程安全的(不能跨线程访问),从而会导致一些问题。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值