QT Eventloop и дескриптор слота? - PullRequest
0 голосов
/ 17 мая 2018

Я создал демо для изучения QObject, QThread и QT Signal / Slot в виде ссылок ниже

Идея такова:

Я создал ExtentQThread, экстент которого из QThread, и запускаю реализацию() функция, которая будет зацикливаться для loopTimeoutMsec (устанавливается в конструкторе) перед вызовом exec () (что заставляет его войти в свой цикл событий потока).Я создал один экземпляр ExtentQThread extQThread1 из основного с loopTimeoutMsec , установленным в 10000 .

Затем я создал два экземпляра ExtentQObject из основного потока. extQObject10 создан и перемещен в extQThread1 и extQObject11 , который не перемещен.

Ожидание теста:

  • Слот extQObject11 запускается в основном потоке примерно (время ожидания = 5000): PASSED
  • Слот extQObject10 работает на extQThread1 : PASSED
  • Слот extQObject10 работает на extQThread1 около ( loopTimeoutMsec = 10000): НЕ ПРОЙДЕНО

[main.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();
}

[exteqthread.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();
}

[stretqobject.cpp]

#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();
}

Вот вывод

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

Как я понимаю, я ожидаю: ExtentQObject :: onExtentQObjectFirstSlot () instance "extQObject10созданный из основного потока, затем перемещенный в extQThread1 ", запущенный в потоке 0x7fdc8aa03700 , работающий со скоростью около 10000 (мсек) вместо 14756 (мсек).поскольку сигнал излучается со скоростью 5000 (мсек), а exec (), который входит в ExtentQthread, запускается после 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 [])

     // 1.0 to test signal of extQObject10 which is moved to extQThread1
     // and signal of extQObject11 which is not moved
+    QTimer timer;
+    timer.setSingleShot(true);
     long timeout = 5000;
-    QTimer::singleShot(timeout, [&extQThread1, &timeout]() {
+    QObject::connect(&timer, &QTimer::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);
+    QObject::connect(&timer, &QTimer::timeout, &extQObject10, &ExtentQObject::onExtentQObjectFirstSlot);
+    QObject::connect(&timer, &QTimer::timeout, &extQObject11, &ExtentQObject::onExtentQObjectFirstSlot);
+    timer.start(timeout);

Ответы [ 2 ]

0 голосов
/ 21 мая 2018

Я нашел причину, может быть, это кому-нибудь пригодится

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, он унаследовал bool QObject :: event (QEvent * e) от QObject, который обрабатывает изменение потока, как показано ниже

    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 , для которого установлено extQThread1 . После этого таймер A начинает работать и срабатывает после установленного для него таймаута , тогда onExtentQObjectFirstSlot будет вызываться для extObject10 .

В итоге, onExtentQObjectFirstSlot будет вызываться для extObject10 после loopTimeoutMsec (установлено для extQThread1) + timeout (установлено для QTimer: : SingleShot)

0 голосов
/ 17 мая 2018

Я не уверен, что вы пытаетесь сделать, но несколько советов:

  • В большинстве случаев вам не нужно переопределять метод run, кроме случаев, когда вам нужноконтролировать активность потока очень тесно.Просто создайте QThread, используйте moveToThread и запустите поток, вот и все.Затем вы можете использовать поток started сигналов или другое соединение сигнал / слот, чтобы убедиться, что оно выполнено в вашем потоке
  • exec будет эффективно запускать цикл событий потока и возвращаться, только если поток существует.
  • Вы можете вручную запустить / остановить цикл обработки событий, используя exec и quit метод
  • Во время sleep или, в более общем случае, когда цикл обработки событий не выполняется, события не будутобработан или слот выполнен.Таким образом, чтобы выполнить слот, вызванный с помощью Invoke или по сигналу из другого потока, обязательно вызвать exec после того, как сообщение поставлено в очередь в цикле событий QThread.

И в целом всегда используйтесигналы / слоты (что вы сделали) для выполнения кода в соответствующем потоке и когда вы можете полагаться на классы более высокого уровня, такие как QtConcurrent.

Для получения более подробной информации вы можете взглянуть на Qtдокументация , а также (немного старше) статья блога

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...