QT Eventloop和插槽手柄?

时间:2018-05-17 03:17:39

标签: qt qthread qobject

我创建了一个演示来研究QObject,QThread和QT Signal / Slot作为下面的链接

这个想法是:

我创建了一个ExtentQThread,其范围来自QThread并实现了run()函数,该函数将在调用exec()之前循环 loopTimeoutMsec (在构造函数上设置)(这使得它进入其线程事件循环)。我从main创建了一个ExtentQThread extQThread1 实例, loopTimeoutMsec 设置为 10000

然后我从主线程创建了两个ExtentQObject实例。创建 extQObject10 并将其移动到 extQThread1 extQObject11 ,但不会移动。

测试期望:

  • extQObject11 slot在主线程上运行(超时= 5000):PASSED
  • extQObject10插槽在 extQThread1上运行:PASSED
  • extQObject10插槽在 extQThread1 周围运行( loopTimeoutMsec = 10000):未通过

[main.cpp中]

unsigned long long

[extentqthread.cpp]

#include <QCoreApplication>

#include <QTimer>
#include "extentqthread.h"

long baseMSecsSinceEpoch;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    baseMSecsSinceEpoch = QDateTime::currentMSecsSinceEpoch();

    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << "Main thread 1" << QThread::currentThreadId();

    // === test1
    ExtentQThread extQThread1("extQThread1 created from main thread", 10000);

    ExtentQObject extQObject10("extQObject10 created from main thread then moved to extQThread1");
    extQObject10.moveToThread(&extQThread1);

    ExtentQObject extQObject11("extQObject11 created from main thread");

    extQThread1.start();

    // 1.0 to test signal of extQObject10 which is moved to extQThread1
    // and signal of extQObject11 which is not moved
    long timeout = 5000;
    QTimer::singleShot(timeout, [&extQThread1, &timeout]() {
        qDebug() << "\n==" << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << "timeout" << timeout
                 << "\n>> To test signal of extQObject10 which is moved to extQThread1 and signal of extQObject11 which is not moved"
                 << "\n>> extQThread1.isRunning()" << extQThread1.isRunning();
    });
    QTimer::singleShot(timeout, &extQObject10, &ExtentQObject::onExtentQObjectFirstSlot);
    QTimer::singleShot(timeout, &extQObject11, &ExtentQObject::onExtentQObjectFirstSlot);

    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << "Main thread 2" << QThread::currentThreadId();

    return a.exec();
}

[extentqobject.cpp]

#include "extentqthread.h"

extern long baseMSecsSinceEpoch;

ExtentQThread::ExtentQThread(QString name_, long loopTimeoutMsec_)
{
    name = name_;
    loopTimeoutMsec = loopTimeoutMsec_;

    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << Q_FUNC_INFO
             << "instance" << name
             << "loopTimeoutMsec" << loopTimeoutMsec
             << "created on thread" << QThread::currentThreadId();
}

void ExtentQThread::run() {
    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << Q_FUNC_INFO
             << "instance" << name
             << "STARTED on thread" << QThread::currentThreadId();

    ExtentQObject extQObject("extQObject created from (" + name + ")");
    connect(this, &ExtentQThread::runFirstSlot, &extQObject, &ExtentQObject::onExtentQObjectFirstSlot);

    if (loopTimeoutMsec < 0) {
        while(1) {};
    } else {
        QThread::msleep(loopTimeoutMsec);
    }

    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << Q_FUNC_INFO
             << "instance" << name
             << "before exec() on thread" << QThread::currentThreadId();

    exec();

    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << Q_FUNC_INFO
             << "instance" << name
             << "after exec() on thread" << QThread::currentThreadId();

    if (loopTimeoutMsec < 0) {
        while(1) {};
    } else {
        QThread::msleep(loopTimeoutMsec);
    }

    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << Q_FUNC_INFO
             << "instance" << name
             << "STOPPED on thread" << QThread::currentThreadId();
}

void ExtentQThread::onExtentQThreadFirstSlot() {
    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << Q_FUNC_INFO
             << "instance" << name
             << "run on thread" << QThread::currentThreadId();

    Q_EMIT runFirstSlot();
}

这是输出

#include "extentqobject.h"

extern long baseMSecsSinceEpoch;

ExtentQObject::ExtentQObject(QString name_)
{
    name = name_;
    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << Q_FUNC_INFO
             << "instance" << name
             << "created on thread" << QThread::currentThreadId();
}

void ExtentQObject::onExtentQObjectFirstSlot() {
    qDebug() << QDateTime::currentMSecsSinceEpoch() - baseMSecsSinceEpoch << Q_FUNC_INFO
             << "instance" << name
             << "run on thread" << QThread::currentThreadId();
}

根据我的理解,我希望: ExtentQObject :: onExtentQObjectFirstSlot()实例“从主线程创建的extQObject10然后移动到extQThread1”在线程0x7fdc8aa03700上运行以10000(msec)而不是14756(msec)运行。因为信号以5000(毫秒)发出,进入ExtentQthread的exec()在10000(毫秒)之后运行,然后它应该处理 onExtentQObjectFirstSlot

有人可以解释一下吗?

////

  • 我尝试从QTimer :: singleShot更改为QTimer实例,它给出了预期的行为(差异如下)
  

diff --git a / main.cpp b / main.cpp       index ed45d23..0ebabf3 100644       --- a / main.cpp       +++ b / main.cpp       @@ -25,14 +25,17 @@ int main(int argc,char * argv [])

0 Main thread 1 0x7fdc8f3f3740
1 ExtentQThread::ExtentQThread(QString, long int) instance "extQThread1 created from main thread" loopTimeoutMsec 10000 created on thread 0x7fdc8f3f3740
1 ExtentQObject::ExtentQObject(QString) instance "extQObject10 created from main thread then moved to extQThread1" created on thread 0x7fdc8f3f3740
1 ExtentQObject::ExtentQObject(QString) instance "extQObject11 created from main thread" created on thread 0x7fdc8f3f3740
1 Main thread 2 0x7fdc8f3f3740
1 virtual void ExtentQThread::run() instance "extQThread1 created from main thread" STARTED on thread 0x7fdc8aa03700
1 ExtentQObject::ExtentQObject(QString) instance "extQObject created from (extQThread1 created from main thread)" created on thread 0x7fdc8aa03700

== 4754 timeout 5000 
>> To test signal of extQObject10 which is moved to extQThread1 and signal of extQObject11 which is not moved 
>> extQThread1.isRunning() true
4754 void ExtentQObject::onExtentQObjectFirstSlot() instance "extQObject11 created from main thread" run on thread 0x7fdc8f3f3740
10001 virtual void ExtentQThread::run() instance "extQThread1 created from main thread" before exec() on thread 0x7fdc8aa03700
14756 void ExtentQObject::onExtentQObjectFirstSlot() instance "extQObject10 created from main thread then moved to extQThread1" run on thread 0x7fdc8aa03700

2 个答案:

答案 0 :(得分:0)

我不确定你要做什么,但有一些建议:

  • 大多数情况下,您不必覆盖run方法,除非您需要非常密切地控制线程活动。只需创建一个QThread,使用moveToThread并启动线程,就是这样。然后,您可以使用线程started信号或其他信号/插槽连接来确保它在您的线程中执行
  • exec将有效地运行线程事件循环,并仅在线程存在时返回。
  • 您可以使用execquit方法
  • 手动运行/停止事件循环
  • sleep,或者更常见的是,当事件循环未运行时,将不会处理任何事件或执行插槽。因此,要执行使用Invoke调用的槽或来自另一个线程的信号,必须在消息在QThread事件循环中排队后调用exec

更一般地说,总是使用信号/插槽(你做了什么)在适当的线程中执行代码,并且当你可以使用QtConcurrent等更高级别的类时依赖它。

有关详细信息,您可以查看Qt documentation以及此更新({3}}

答案 1 :(得分:0)

我找到了原因,可能这对某人有帮助

QTimer::singleShot(timeout, &extQObject10, &ExtentQObject::onExtentQObjectFirstSlot);

在这种情况下,QTimer :: singleShot调用此重载函数

QSingleShotTimer::QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, QtPrivate::QSlotObjectBase *slotObj)
    : QObject(QAbstractEventDispatcher::instance()), hasValidReceiver(r), receiver(r), slotObj(slotObj)
{

    timerId = startTimer(msec, timerType);
    if (r && thread() != r->thread()) {
        // Avoid leaking the QSingleShotTimer instance in case the application exits before the timer fires
        connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
        setParent(0);
        moveToThread(r->thread());
    }
}

这创建了一个计时器实例(让我们调用timerA),因为 extQObject10 ,它是为QTimer :: singleShot设置的接收器,被移动到 extThread1 ,所以timerA被移动到那里太

因为QTimer扩展了QObject所以它从QObject继承了 bool QObject :: event(QEvent * e),它处理如下的线程更改

    case QEvent::ThreadChange: {
    Q_D(QObject);
    QThreadData *threadData = d->threadData;
    QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.load();
    if (eventDispatcher) {
        QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this);
        if (!timers.isEmpty()) {
            // do not to release our timer ids back to the pool (since the timer ids are moving to a new thread).
            eventDispatcher->unregisterTimers(this);
            QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection,
                                      Q_ARG(void*, (new QList<QAbstractEventDispatcher::TimerInfo>(timers))));
        }
    }
    break;

这里, QMetaObject :: invokeMethod 通过 extQThread1 生成一个槽句柄,因此它将在 loopTimeoutMsec 之后处理,它被设置为< EM> extQThread1 。之后,timerA开始运行并在为其设置 timeout 之后触发,此时 onExtentQObjectFirstSlot 将在 extObject10 上调用。

总之,在 loopTimeoutMsec (为extQThread1设置)+ timeout 之后,将在 extObject10 上调用 onExtentQObjectFirstSlot (为QTimer :: singleShot设置