Qt: deadlock после вызова QEventLoop :: exec

У меня есть довольно простое приложение, которое кажется заблокированным при определенных условиях, когда я вызываю QEventLoop :: exec. Приложение вызывает эту функцию в двух сценариях:

  • когда определенные данные поступают на сокет
  • после события таймера

в обоих случаях он используется в следующем контексте (просто запрос http, на самом деле ничего особенного):

QNetworkReply::NetworkError HttpGetMessagesStrategy::syncHttp(const QUrl url, QByteArray &dst) const
{
QNetworkRequest request(url);
request.setRawHeader("Cache-Control", "no-cache");

QNetworkAccessManager mgr;
QEventLoop eventLoop;
QObject::connect(&mgr, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit()));
QNetworkReply *reply = mgr.get(request);
if (reply == NULL) {
return QNetworkReply::UnknownNetworkError;
}

eventLoop.exec();

QNetworkReply::NetworkError error = reply->error();
if (error == QNetworkReply::NoError) {
dst += reply->readAll();
}
delete reply;

return error;
}

Вот что происходит, когда он пытается это вызвать:

...
#56 0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
#57 0x0000003404b804a2 in ?? () from /usr/lib64/libQtCore.so.4
#58 0x0000003404b7d928 in ?? () from /usr/lib64/libQtCore.so.4
#59 0x00000033fba38f0e in g_main_context_dispatch () from /lib64/libglib-2.0.so.0
#60 0x00000033fba3c938 in ?? () from /lib64/libglib-2.0.so.0
#61 0x00000033fba3ca3a in g_main_context_iteration () from /lib64/libglib-2.0.so.0
#62 0x0000003404b7d5f3 in QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#63 0x0000003404b56722 in QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#64 0x0000003404b569ec in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () from /usr/lib64/libQtCore.so.4
#65 0x00007f41c1b4eec7 in HttpGetMessagesStrategy::syncHttp (this=<value optimized out>, url=<value optimized out>, dst=...) at HttpGetMessagesStrategy.cpp:49
#70 0x000000000040e57c in DevicePlugin::timerEvent (this=0x1267390, event=0x7fff902bb5f0) at DevicePlugin.cpp:250
#71 0x0000003404b6698e in QObject::event(QEvent*) () from /usr/lib64/libQtCore.so.4
#72 0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
...

Как можно увидеть вместо ожидания ответа на HTTP-запрос, он взял другое событие и начал его обрабатывать. В результате я получаю огромное количество кадров (746) в текущем потоке до того, как мое приложение останавливается, и затем я вижу такие строки:

#0  0x00000033faa0efe0 in __pause_nocancel () from /lib64/libpthread.so.0
#1  0x00000033faa0917b in __pthread_mutex_lock_full () from /lib64/libpthread.so.0
#2  0x0000003404a702a3 in ?? () from /usr/lib64/libQtCore.so.4
#3  0x0000003404a6cd95 in QMutex::lock() () from /usr/lib64/libQtCore.so.4
#4  0x0000003404b57952 in QCoreApplication::postEvent(QObject*, QEvent*, int) () from /usr/lib64/libQtCore.so.4
#5  0x000000000040e293 in DevicePlugin::destroyConnection (this=0x1267390, c=0x1a85fb0) at DevicePlugin.cpp:194
#6  0x000000000040e5b6 in DevicePlugin::timerEvent (this=0x1267390, event=0x7fff902b9230) at DevicePlugin.cpp:254
#7  0x0000003404b6698e in QObject::event(QEvent*) () from /usr/lib64/libQtCore.so.4
#8  0x0000003404b57cdc in QCoreApplication::notifyInternal(QObject*, QEvent*) () from /usr/lib64/libQtCore.so.4
...

Может ли кто-нибудь объяснить мне, что я здесь делаю не так?

1

Решение

http://qt-project.org/doc/qt-4.8/qeventloop.html#exec

int QEventLoop :: exec (ProcessEventsFlags flags = AllEvents) входит
основной цикл обработки событий и ожидает вызова метода exit (). Возвращает
значение, которое было передано для выхода ().

exec () является представлением обработки события QT и всегда является блокирующим вызовом до тех пор, пока текущий поток не вернется с помощью exit (). Это позволяет потокам вне основного цикла exec () использовать сигналы QT и слоты без конфликтов.

Вы должны соединить ваш вызов Finish () с вашим собственным слотом обработки, определенным в вашем заголовочном файле.

Не забудьте наследовать каждый класс от QObject (или любого другого объекта QT), если вы хотите использовать Сигналы и Слоты и использовать Макро QOBJECT.

0

Другие решения

У вас есть два цикла событий. Второй цикл событий не блокирует первый — он просто блокирует текущий метод, поэтому обработка не может вернуться к первому циклу событий. Однако, когда данные поступают в сокет, событие запускается, и ваш метод запускается снова. Кажется, единственное решение — изменить дизайн, чтобы он работал без блокировки syncHttp метод, и перепишите все взаимодействие с этим методом, используя сигналы & слоты.

Для exec идиома, из ссылка, которую вы указали:

Предупреждение: для тестовых случаев этот подход работает хорошо. Для реальных приложений, избегайте его использования. Лучше разделить функцию, которая хочет ждать, на две части, а обрабатывать сигнал — это отдельный слот.

Если у вас есть приложение, управляемое событиями (и, как правило, у вас есть, если вы используете Qt), и вам вдруг нужно заблокировать его таким образом — это означает, что существует проблема проектирования.

0

Вы пытаетесь использовать QNetworkManager а также QNetworkReply блокирующим способом, который не был предназначен для этого.

Рассмотреть возможность использования QTcpSocket вместо этого или перепроектируйте вашу программу, чтобы использовать асинхронный механизм.

0
По вопросам рекламы [email protected]