Quantcast
Channel: सत्यं वद धर्मं चर » select
Viewing all articles
Browse latest Browse all 2

Использование произвольного диспетчера событий в QNetworkAccessManager

$
0
0

Изменение поведения работы без изменения кода библиотеки

Начиная с Qt 4.8 операции с HTTP стали многопоточными. Как известно, до Qt 5 не было возможности задать свой диспетчер событий для создаваемых потоков; в Qt 5 такая возможность появилась. Тем не менее, пока что нет возможности сказать Qt, что для всех создаваемых потоков нужно использовать такой-то диспетчер событий: без вызова QThread::setEventDispatcher() будет использоваться диспетчер событий по умолчанию. Для это либо QEventDispatcherUNIX, использующий (), либо QEventDispatcherGLib, использующий (). Производительность этих диспетчеров примерно одинакова и далека от идеальной.

Хорошее представление о стеке HTTP в Qt даёт эта статья. В приведённых диаграммах видно, что Qt создаёт отдельный поток для выполнения HTTP-запросов (поток может выполнять до шести параллельных соединений к одному серверу).

В принципе, шесть параллельных соединений — это не повод использовать () вместо select(); тем не менее, мне была интересна реализация вмешательства во внутреннюю кухню Qt, не трогая при этом код библиотеки.

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

Когда QNetworkAccessManager (точнее, один из классов стека HTTP) создаёт поток, он даёт ему имя: bearerThread, httpThread, Thread (pooled); возможно, что используются и другие имена. Имена задаются посредством вызова QObject::setObjectName(); как видно из документации, при изменении имени объекта вызывается (испускается?) сигнал objectNameChanged(const QString& objectName).

Тут нужно сделать отступление: задать потоку диспетчер событий можно только до сигнала QThread::started(). Фактически, objectNameChanged() является единственным сигналом, который гарантированно вызывается до запуска потока. Поэтому нужно каким-то образом перехватить данный сигнал, убедиться, что он вызывается из класса, являющегося или порождённым от QThread, почел чего создать свой диспетчер событий и установить его потоку. Проблема только в том, что нет документированного метода перехвата вызова сигналов, в результате чего придётся немного покопаться во внутренностях moc.

Если посмотреть на реализацию сигнала, сгенерированную moc, то видно, что она сводится к вызову метода QMetaObject::activate(). Что интересно, этот метод абсолютно не документирован. В коде видно, что реализация метода ссылается на интересную структуру под именем qt_signal_spy_callback_set. Дальше дело техники.

В qobject_p.h есть такие замечательные объявления:

/* for Qt Test */
struct QSignalSpyCallbackSet
{
    typedef void (*BeginCallback)(QObject *caller, int signal_or_method_index, void **argv);
    typedef void (*EndCallback)(QObject *caller, int signal_or_method_index);
    BeginCallback signal_begin_callback,
                    slot_begin_callback;
    EndCallback signal_end_callback,
                slot_end_callback;
};
void Q_CORE_EXPORT qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set);

extern QSignalSpyCallbackSet Q_CORE_EXPORT qt_signal_spy_callback_set;

Пометка «for Qt Test» сама по себе подсказывает, где нужно смотреть примеры использования данной возможности.
Функция qt_signal_spy_callback_set() реализована в qcoreapplication.cpp с пометкой «Support for introspection»

BeginCallback вызывается перед вызовом сигналов и слотов, EndCallback — после.

В testlib/qsignaldumper.cpp находим пример использования данной функции.

В результате получаем такой код:

#include <QtCore/QCoreApplication>
#include <QtCore/QMetaMethod>
#include <QtCore/QThread>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QtCore/private/qobject_p.h>
#include <QtCore/private/qmetaobject_p.h>
#include <QtCore/private/qeventdispatcher_unix_p.h>
#include <stdio.h>

static void signal_begin_callback(QObject* caller, int signal_index, void** argv)
{
    Q_ASSERT(caller != 0);
    Q_UNUSED(argv);

    const QMetaObject* mo = caller->metaObject();
    Q_ASSERT(mo != 0);

    QMetaMethod member = QMetaObjectPrivate::signal(mo, signal_index);
    Q_ASSERT(member.isValid());

    if (!strcmp(mo->className(), "QThread") && member.name() == "objectNameChanged") {
        QThread* thr = qobject_cast<QThread*>(caller);
        thr->setEventDispatcher(new QEventDispatcherUNIX());
    }

    QByteArray res;
    res.append(mo->className()).append("::").append(member.name());
    printf("%s\n", res.constData());
    fflush(stdout);
}

class MyApplication : public QCoreApplication {
    Q_OBJECT
public:
    MyApplication(int& argc, char** argv) : QCoreApplication(argc, argv) {}

    int exec(void)
    {
        QSignalSpyCallbackSet callbacks = { 0, 0, 0, 0 };
        callbacks.signal_begin_callback = signal_begin_callback;
        qt_register_signal_spy_callbacks(callbacks);

        QNetworkAccessManager* mgr = new QNetworkAccessManager(this);
        QNetworkRequest req(QUrl(QLatin1String("http://www.google.com/")));
        QNetworkReply* reply = mgr->get(req);
        QObject::connect(reply, SIGNAL(finished()), this, SLOT(finished()));

        return QCoreApplication::exec();
    }

public Q_SLOTS:
    void finished(void)
    {
        QSignalSpyCallbackSet callbacks = { 0, 0, 0, 0 };
        qt_register_signal_spy_callbacks(callbacks);
        QCoreApplication::quit();
    }
};

#include "main.moc"

int main(int argc, char *argv[])
{
    MyApplication a(argc, argv);
    return a.exec();
}

Файл проекта:

QT      += core network core-private
QT      -= gui
TARGET   = spytest
CONFIG  += console
CONFIG  -= app_bundle
TEMPLATE = app
SOURCES += main.cpp

У меня Qt собрана с поддержкой GLib, поэтому с закомментированным вызовом thr->setEventDispatcher(new QEventDispatcherUNIX()); получается такая картина:

QThread::objectNameChanged
QThread::started
QEventDispatcherGlib::aboutToBlock
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QFile::aboutToClose
QObject::destroyed
QFile::aboutToClose
QObject::destroyed
QFile::aboutToClose
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QGenericEngine::configurationAdded
QGenericEngine::configurationAdded
QGenericEngine::updateCompleted
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QNetworkConfigurationManagerPrivate::configurationAdded
QNetworkConfigurationManagerPrivate::onlineStateChanged
QObject::destroyed
QObject::destroyed
QObject::destroyed
QNetworkConfigurationManagerPrivate::configurationAdded
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QThread::objectNameChanged
QNetworkReplyHttpImpl::startHttpRequest
QEventDispatcherGlib::aboutToBlock
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QThread::started
QEventDispatcherGlib::aboutToBlock
QThread::objectNameChanged
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QThread::started
QObject::destroyed
QHostInfoResult::resultsReady
QObject::destroyed
QTcpSocket::stateChanged
QTcpSocket::stateChanged
QTcpSocket::hostFound
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QTcpSocket::stateChanged
QTcpSocket::connected
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QTcpSocket::readyRead
QHttpNetworkReply::headerChanged
QHttpThreadDelegate::downloadMetaData
QHttpNetworkReply::readyRead
QHttpThreadDelegate::downloadData
QHttpNetworkReply::dataReadProgress
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QHttpNetworkReply::finished
QHttpThreadDelegate::downloadFinished
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QObject::destroyed
QObject::destroyed
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QNetworkReplyHttpImpl::metaDataChanged
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QNetworkReplyHttpImpl::readyRead
QNetworkReplyHttpImpl::downloadProgress
QNetworkReplyHttpImpl::downloadProgress
QNetworkReplyHttpImpl::readChannelFinished
QNetworkReplyHttpImpl::finished
QNetworkAccessManager::finished

Видно, что везде после вызова QThread::started() проявляется QEventDispatcherGlib. Раскомментируем вызов setEventDispatcher() и получим что-то такое:

QThread::objectNameChanged
QThread::started
QEventDispatcherUNIX::awake
QEventDispatcherUNIX::aboutToBlock
QFile::aboutToClose
QObject::destroyed
QFile::aboutToClose
QObject::destroyed
QFile::aboutToClose
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QObject::destroyed
QEventDispatcherUNIX::awake
QGenericEngine::configurationAdded
QGenericEngine::configurationAdded
QGenericEngine::updateCompleted
QEventDispatcherUNIX::awake
QNetworkConfigurationManagerPrivate::configurationAdded
QNetworkConfigurationManagerPrivate::onlineStateChanged
QNetworkConfigurationManagerPrivate::configurationAdded
QObject::destroyed
QEventDispatcherUNIX::aboutToBlock
QEventDispatcherUNIX::awake
QEventDispatcherUNIX::aboutToBlock
QObject::destroyed
QObject::destroyed
QThread::objectNameChanged
QThread::started
QEventDispatcherUNIX::awake
QEventDispatcherUNIX::aboutToBlock
QNetworkReplyHttpImpl::startHttpRequest
QEventDispatcherUNIX::awake
QEventDispatcherGlib::aboutToBlock
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QThread::objectNameChanged
QEventDispatcherUNIX::aboutToBlock
QThread::started
QObject::destroyed
QHostInfoResult::resultsReady
QObject::destroyed
QEventDispatcherUNIX::awake
QTcpSocket::stateChanged
QTcpSocket::stateChanged
QTcpSocket::hostFound
QEventDispatcherUNIX::aboutToBlock
QTcpSocket::stateChanged
QTcpSocket::connected
QEventDispatcherUNIX::awake
QEventDispatcherUNIX::aboutToBlock
QEventDispatcherUNIX::awake
QEventDispatcherUNIX::aboutToBlock
QTcpSocket::readyRead
QHttpNetworkReply::headerChanged
QHttpThreadDelegate::downloadMetaData
QHttpNetworkReply::readyRead
QHttpThreadDelegate::downloadData
QHttpNetworkReply::dataReadProgress
QEventDispatcherUNIX::awake
QHttpNetworkReply::finished
QHttpThreadDelegate::downloadFinished
QEventDispatcherUNIX::awake
QEventDispatcherUNIX::awake
QObject::destroyed
QObject::destroyed
QEventDispatcherUNIX::aboutToBlock
QNetworkReplyHttpImpl::metaDataChanged
QNetworkReplyHttpImpl::readyRead
QNetworkReplyHttpImpl::downloadProgress
QEventDispatcherGlib::awake
QEventDispatcherGlib::aboutToBlock
QNetworkReplyHttpImpl::downloadProgress
QNetworkReplyHttpImpl::readChannelFinished
QNetworkReplyHttpImpl::finished
QNetworkAccessManager::finished

Видим, что теперь используется QEventDispatcherUNIX. поэтму будем считать, что proof of concept удался.

© 2014 सत्यं वद धर्मं चर. Все права защищены. Перепубликация материалов без разрешения автора запрещена.

При использовании материалов блога наличие активной не закрытой от индексирования ссылки на источник обязательно.


Viewing all articles
Browse latest Browse all 2

Latest Images

Pangarap Quotes

Pangarap Quotes

Vimeo 10.7.0 by Vimeo.com, Inc.

Vimeo 10.7.0 by Vimeo.com, Inc.

HANGAD

HANGAD

MAKAKAALAM

MAKAKAALAM

Doodle Jump 3.11.30 by Lima Sky LLC

Doodle Jump 3.11.30 by Lima Sky LLC

Doodle Jump 3.11.30 by Lima Sky LLC

Doodle Jump 3.11.30 by Lima Sky LLC

Trending Articles


Pokemon para colorear


Winx Club para colorear


Girasoles para colorear


Rana para colorear


Renos para colorear


Dromedario para colorear


People Walk Away Quotes, Inspire Quotes


Inspirational Tagalog quotes and Motivational English Quotes


Tagalog Inspirational Quotes


Mga Tala sa “Unang Siglo ng Nobela sa Filipinas” (2009) ni Virgilio S. Almario


Scooby doo para colorear


Libros para colorear


Mandalas de flores para colorear


Dibujos para colorear de perros


Mariquitas para colorear


Gwapo Quotes : Babaero Quotes


Dear Ex Quotes, Sakit Quotes


Long Distance Relationship Tagalog Love Quotes


RE: Mutton Pies (mely)


Ang Nobela sa “From Darna to ZsaZsa Zaturnnah: Desire and Fantasy, Essays on...





Latest Images

Pangarap Quotes

Pangarap Quotes

Vimeo 10.7.0 by Vimeo.com, Inc.

Vimeo 10.7.0 by Vimeo.com, Inc.

HANGAD

HANGAD

MAKAKAALAM

MAKAKAALAM

Doodle Jump 3.11.30 by Lima Sky LLC

Doodle Jump 3.11.30 by Lima Sky LLC

Doodle Jump 3.11.30 by Lima Sky LLC

Doodle Jump 3.11.30 by Lima Sky LLC