Qt- QNetworkReply удалить оператор- сбой во время выполнения

#qt #network-programming #qnetworkaccessmanager

#qt #сетевое программирование #qnetworkaccessmanager

Вопрос:

В моем приложении Qt я использую QNetworkAccessManager в потоке, чтобы мой основной поток мог свободно выполнять свою задачу. Для каждой выполняемой мной операции get я сохраняю QNetworkReply * в списке и после получения ответа извлекаю его из своего списка, удаляю запись в списке и вызываю deleteLater() для объекта QNetworkReply *. Однако после пары запросов / ответов вот сбой, который я получаю во время выполнения:

Код, который я использовал, является:

 void NetworkManager::responseFromServer(QNetworkReply* pReply)
{

  // Retrieve the TileRequestMessage.
  QImage *pImage = imageMapper.value(pReply);
  // Get the bytes from the response.
  QByteArray byteArray = pReply->readAll();
  // Load the QImage with the data.
  bool loaded = pImage->loadFromData(byteArray);

  // Remove the request from book-keeping.
  imageMapper.remove(mapIterator.key());
  pReply->deleteLater();
  return;
}
  

где PImage — это указатель на объект типа QImage. Объект создается заранее, и его указатель, сопоставленный с QNetworkReply*, сохраняется в QMap.

Ошибка, которую я получаю, является:

Остановлено на 0x637837aa (удаление оператора) в потоке 1 (отсутствует отладочная информация). Сексуальное исключение в 0x637837aa, код: 0xc0000005: нарушение доступа для чтения в: 0xffffffffcdcdcdcdc1, флаги = 0x0

Стек вызовов является:

0 оператор удаляет MSVCR90D 0 0x637837aa
1 QList::node_destruct qlist.h 418 0x64071704
2 QList::free qlist.h 744 0x6407153b
3 QList::~QList qlist.h 718 0x64070b1f
4 QQueue ::~QQueue qqueue.h 58 0x6407076f
5 QNetworkReplyImplPrivate::Уведомления об обработке qnetworkreplyimpl.cpp 358 0x6406c99d
6 QNetworkReplyImpl::событие qnetworkreplyimpl.cpp 868 0x6406e646
7 QApplicationPrivate::notify_helper qapplication.cpp 4445 0x6507153e
8 qприложение::уведомлять qapplication.cpp 3845 0x6506f1ba
9 QCoreApplication::notifyInternal qcoreapplication.cpp 732 0x671c2fb1
10 QCoreApplication::SendEvent qcoreapplication.h 215 0x671c8159
11 QCoreApplicationPrivate::Отправленные события qcoreapplication.cpp 1373 0x671c3f0b
12 qt_internal_proc qeventdispatcher_win.cpp 506 0x67206bf9
13 Является составной потоком USER32 0 0x77bb86ef
14 Является составной потоком USER32 0 0x77bb8876
15 Является составной потоком USER32 0 0x77bb89b5
16 DispatchMessageW USER32 0 0x77bb8e9c
17 QEventDispatcherWin32::Обрабатывает события qeventdispatcher_win.cpp 807 0x67207b96
18 QEventLoop::processEvents qeventloop.cpp 150 0x671c0abe
19 QEventLoop::exec qeventloop.cpp 201 0x671c0bf0
20 QThread::exec qthread.cpp 490 0x670643d6
21 DispatcherThread::выполнить DispatcherThread.cpp 226 0x1001031a
22 QThreadPrivate::start qthread_win.cpp 317 0x6706852f
23 beginthreadex MSVCR90D 0 0x636edff3
24 beginthreadex MSVCR90D 0 0x636edf89
25 BaseThreadInitThunk kernel32 0 0x77191194
26 RtlInitializeExceptionChain ntdll 0 0x77ccb429
27 RtlInitializeExceptionChain ntdll 0 0x77ccb3fc 0x77ccb3fc

Я использую msvc для компиляции своего Qt-кода. Есть какие-нибудь сведения о том, в чем может заключаться проблема??

Спасибо,

Вишну.

Комментарии:

1. Может помочь некоторый код о том, как вы управляете (и удаляете) объектами QNetworkReply.

2. изменен вопрос, чтобы добавить код..

Ответ №1:

Не глядя на ваш фактический код и основываясь на вашем описании ошибки, вполне возможно, что вы удаляете QNetworkReply до того, как он отправил finished сигнал. Таким образом, после удаления, когда становятся доступными новые данные, QNetworkReply выдает readyRead сигнал, который означает, что он пытается получить доступ к уже удаленной записи и, следовательно, ошибки «нарушение доступа для чтения».

Комментарии:

1. Именно по этой причине я вызываю метод deleteLater() в моем слоте. Кроме того, мне интересно, как QNetworkReply может выдавать сигнал readyRead после удаления объекта QNetworkReply.

2. Также, при дальнейшем исследовании, я обнаружил, что иногда сигнал finished () QNetworkReply * срабатывает после удаления сигнала finished() QNetworkAccessManager (где QNetworkReply *), и, таким образом, отправитель в моем слоте, подключенном к сигналу finished() в QNetworkReply *, помечен 0x00. Если это так, куда мне следует удалить мой объект QNetworkReply * ???

3. Что касается вашего первого комментария, объект QNetworkReply создан не вами, а получен в QNetworkAccessManager::get . Таким образом, это означает, что у вас нет контроля над созданным таким образом объектом QNetworkReply, и, следовательно, он может выдавать readyRead сигнал всякий раз, когда появляются новые данные, доступные для чтения.

4. Пожалуйста, обратитесь к документации по QNetworkAccessManager::finished ссылке на слот . Четко указано, что не следует выполнять delete объект QNetworkReply в этом слоте. Возможно, вы можете попробовать удалить объект QNetworkReply с помощью deleteLater() в конце всех обращений к сети.

5. Перед выполнением deleteLater() над объектом QNetworkReply вы можете проверить, завершил ли он обработку с помощью isFinished() .

Ответ №2:

Просто идея:

Поскольку вы используете deleteLater(), вы не знаете, когда произойдет удаление и, следовательно, когда указатель QNetworkReply * может быть недействительным в вашем списке.

Таким образом, возможно, попробуйте обернуть ваш указатель в защищенный указатель (QPointer), а затем просто удалите его из списка, если он удален / null. Если это все еще действительный указатель, вы вызываете deleteLater();