Связь между циклом событий Qt и потоками

Давайте сначала поговорим о ключевых моментах и ​​сначала поймем несколько важных концепций.

  • Планировщик событий, конкретная реализация планировщика связана с операционной системой.Различные операционные системы имеют разные реализации.Например, реализация планировщика в системе Linux — QEventDispatcherUNIX, а их реализация под окном — QEventDispatcherWin32.Их каталог — Просто найдите его в каталоге QTDIR/5.15.2/Src/qtbase/src/corelib/kernel. Этот планировщик реализует основные функции цикла событий. Базовым классом диспетчеров событий в разных операционных системах является один и тот же QAbstractEventDispatcher.
  • threadData, назовем это здесь данными потока. Каждый QObject имеет такой элемент данных. Отсюда мы видим, что каждый объект QObject связан с потоком. ThreadData содержит информацию о потоке, которому принадлежит объект, включая очередь событий потока и т. д. Этот элемент данных можно передать через qobject::movetoThread.
  • Каждый поток может запускать несколько циклов событий (QEventLoop), но все QEventLoop используют один и тот же планировщик событий, поэтому даже если в одном потоке запускается несколько событий, планировщик событий в конечном цикле событий один и тот же.
  • Основная функция цикла событий: Насколько я понимаю, это перемещение по очереди событий этого потока и отправка событий в очереди сообщений соответствующему объекту для обработки. Сообщения в очереди событий могут быть пользовательскими событиями, отправленными пользователем, или они могут быть запланированы. Событием сервера также может быть удаление объекта или событие, преобразованное из некоторых сообщений, отправленных операционной системой (например, операция нажатия кнопки и т. д.).
  • Существует концепция сжатия событий в очереди событий. Концепция сжатия заключается в сжатии событий с одинаковым идентификатором события (например, событий таймера) и одного и того же объекта в очереди событий в одно событие.

Говорить особо нечего, давайте перейдем непосредственно к исходному коду и наглядно покажем взаимосвязь между циклом событий и потоками через код.

Цикл событий основного потока запускается следующим кодом

QCoreApplication::exec()

Внутренняя реализация exec выглядит следующим образом:

int QCoreApplication::exec()
{
...
    QEventLoop eventLoop;
    int returnCode = eventLoop.exec();
...
    return returnCode;
}

Как видно из приведенного выше кода, определен объект QEventLoop, и для этого объекта вызывается метод exec. Краткая реализация exec QEventLoop выглядит следующим образом:

int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
...
    while (!d->exit)
        processEvents(flags | WaitForMoreEvents | EventLoopExec);
...
    return d->returnCode;
}

Как видно из вышеизложенного, exec в QEventLoop вызывает методprocessEvents. Реализация этого метода заключается в следующем:

bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (!d->threadData->eventDispatcher)
        return false;
    if (flags & DeferredDeletion)
        QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
    return d->threadData->eventDispatcher->processEvents(flags);
}

Как видно из вышеизложенного, при реализации процессаEvent сначала оценивается, существует ли член eventDispathcer в threadData. Если он не существует, возвращается false. EventDispatcher понимается буквально как распределитель событий, а его метод ProcessEvents — это вызывается через указатель объекта.Здесь возникнет вопрос, когда был создан eventDispatcher? Пожалуйста, посмотрите на код ниже:

QEventLoop::QEventLoop(QObject *parent)
    : QObject(*new QEventLoopPrivate, parent)
{
    Q_D(QEventLoop);
    if (!QCoreApplication::instance() && QCoreApplicationPrivate::threadRequiresCoreApplication()) {
        qWarning(
"QEventLoop: Cannot be used without QApplication");
    } else {
        d->threadData.loadRelaxed()->ensureEventDispatcher();
    }
}
QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
{
    Q_UNUSED(data);
#if defined(Q_OS_DARWIN)
    bool ok = false;
    int value = qEnvironmentVariableIntValue("QT_EVENT_DISPATCHER_CORE_FOUNDATION", &ok);
    if (ok && value > 0)
        return new QEventDispatcherCoreFoundation;
    else
        return new QEventDispatcherUNIX;
#elif !defined(QT_NO_GLIB)
    const bool isQtMainThread = data->thread.loadAcquire() == QCoreApplicationPrivate::mainThread();
    if (qEnvironmentVariableIsEmpty(
"QT_NO_GLIB")
        && (isQtMainThread || qEnvironmentVariableIsEmpty(
"QT_NO_THREADED_GLIB"))
        && QEventDispatcherGlib::versionSupported())
        return new QEventDispatcherGlib;
    else
        return new QEventDispatcherUNIX;
#else
    return new QEventDispatcherUNIX;
#endif
}

 

Как видите, eventDispatcher создается во время построения объекта QEventLoop.Обратите внимание, что сгенерированный здесь eventDispatcher уже имеет тип QEventDispatcherUnix . Следующим шагом является перечисление базовой реализации планировщика событий. Здесь представлен Linux, поэтому реализована реализация процессаEvents в QEventDispatcher UNIX. Исходный код выглядит следующим образом:

bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherUNIX);
    ……
    QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
    ……
    timespec *tm = nullptr;
    ……
    d->pollfds.append(d->threadPipe.prepare());

    int nevents = 0;

    switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) {
    ……
    }
    return (nevents > 0);
}

void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
                                               QThreadData *data)
{
    ……
    while (i < data->postEventList.size()) {
        ……
        const QPostEvent &pe = data->postEventList.at(i);
        ++i;
        ……
        QEvent *e = pe.event;
        QObject * r = pe.receiver;
        ……
        QCoreApplication::sendEvent(r, e);
    }
    ……
}

Как видно из приведенного выше кода, в реализацииprocessEvents в планировщике событий сначала вызывается метод sendPostedEvents для отправки событий в очереди событий соответствующему объекту через вызов QCoreApplication::sendEvent. метод - вызовы метода блокировки используются только для вызовов внутри этого потока.Его внутренняя основная реализация - это вызов функции, то есть вызов метода события объекта через указатель получателя объекта.Однако следует отметить, что перед вызовом сначала метод события. Чтобы вызвать метод фильтра событий объекта прослушивателя событий, зарегистрированного этим объектом. Я выделю отдельную статью, чтобы объяснить sendevent, и в ней будет реализована подробная реализация.

Затем был вызван метод, связанный с опросом, который включал соответствующее использование threadPipe.Проверив информацию, я узнал, что следующая реализация приведет к тому, что цикл откажется от процессора и будет ждать появления члена в событии очередь, прежде чем приступить к работе. Мне это нужно. Буду изучать дальнейшие дополнения.

Подведем итог

На этом этапе мы понимаем природу цикла событий и взаимосвязь между циклами событий и потоками:

  1. Циклы событий неотделимы от потоков. Хотя в потоке может быть запущено несколько циклов событий, эти циклы событий используют одни и те же данные потока. То есть независимо от того, сколько циклов событий запускается в потоке, все они работают в одном потоке. Планировщик событий не приведет к тому, что другие очереди событий не смогут получать данные, запуская несколько очередей событий.
  2. Суть цикла событий заключается в отправке событий в очереди событий по одному каждому объекту в этом потоке для обработки.
  3. Каждый объект QObject связан с потоком.Каждый объект QObject имеет член threadData, который содержит информацию о потоке, которому принадлежит объект.


 

Guess you like

Origin blog.csdn.net/iqanchao/article/details/132761977