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

В моем приложении Qt я использую QNetworkAccessManager в потоке, чтобы мой основной поток оставался свободным для выполнения своей задачи. Для каждой операции получения, которую я выполняю, я сохраняю 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 (отсутствует отладочная информация). sException по адресу 0x637837aa, код: 0xc0000005: нарушение доступа для чтения по адресу: 0xffffffffcdcdcdc1, flags=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
QQueue :: ~ QQueue QQueue.h 58 0x6407076F
5 qnetworkreplyimplplplivate :: handlenotifications qnetworkreplyimpl.cpp 358 0x6406C99D
6 qnetworkreplyimpl :: Event qnetworkreplyimpl.cpp 868 0x6406e646
7 qapplicationPrivate :: Notify_Helper qapplication.cpp 4445 0x6507153e BR> 8 Qapplation :: Notify Qapplication.cpp 3845 0x6506f1ba
9 QCOReApplication :: NotifyInternal QCOReApplication.cpp 732 0x671C2FB1
10 qCoreApplication :: SendEvent QCOREAP.H 215 0x671C8159
11 QCOREAP 6771C8159. 0x671c3f0b
12 qt_internal_proc qeventdispatcher_win.cpp 506 0x67206bf9
13 IsThreadDesktopComposited USER32 0 0x77bb86ef
14 IsThreadDesktopComp usic user32 0 0x77bb8876
15 istreaddesktopcomposited user32 0 0x77bb89b5
16 dispatchmessagew user32 0 0x77bb8e9c
17 qeventdispatcherwin32 :: Процессы qeventddispatcher_win.cpp 807 0x67207b96
18 qeventloop :: Процессватели Qeventloop.cpp 150 0x671c0abe
19 QEventLoop::exec qeventloop.cpp 201 0x671c0bf0
20 QThread::exec qthread.cpp 490 0x670643d6
21 DispatcherThread::run DispatcherThread.cpp 226 0x1001031a
22 QThreadPrivate::<6c8f27xread106.win > 23 beginthreadex MSVCR90D 0 0x636edff3
24 beginthreadex MSVCR90D 0 0x636edf89
25 BaseThreadInitThunk kernel32 0 0x77191194
26 RtlInitializeExceptionChain ntdll 0 0x77ccb429
27 RtlInitializeExceptionF

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

Спасибо,

Вишну.


person Vishnu Pedireddi    schedule 03.05.2011    source источник
comment
Некоторый код о том, как вы управляете (и удаляете) объектами QNetworkReply, может помочь.   -  person Frank Osterfeld    schedule 03.05.2011
comment
изменил вопрос, чтобы добавить код..   -  person Vishnu Pedireddi    schedule 03.05.2011


Ответы (2)


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

person Purnima    schedule 03.05.2011
comment
Именно по этой причине я вызываю метод deleteLater() в своем слоте. Кроме того, мне интересно, как QNetworkReply может выдавать сигнал readyRead после удаления объекта QNetworkReply. - person Vishnu Pedireddi; 03.05.2011
comment
Кроме того, при дальнейшем расследовании я обнаружил, что иногда сигнал Finish() QNetworkReply* срабатывает после того, как сигнал Finish() QNetworkAccessManager (где QNetworkReply*) удаляется, и, таким образом, отправитель в моем слоте подключается к сигналу Finish() в QNetworkReply. * помечен 0x00. Если это так, где я должен удалить свой объект QNetworkReply*??? - person Vishnu Pedireddi; 03.05.2011
comment
Для вашего первого комментария объект QNetworkReply создан не вами, а получен в файле QNetworkAccessManager::get. Таким образом, это означает, что у вас нет контроля над созданным таким образом объектом QNetworkReply, и, следовательно, он может выдавать сигнал readyRead всякий раз, когда появляются новые данные, доступные для чтения. - person Purnima; 04.05.2011
comment
См. документацию по ссылке слота QNetworkAccessManager::finished. Четко указано, что нельзя выполнять delete объекта QNetworkReply в этом слоте. Так что, возможно, вы можете попробовать удалить объект QNetworkReply, используя deleteLater() в конце всех сетевых доступов. - person Purnima; 04.05.2011
comment
Прежде чем выполнять deleteLater() для объекта QNetworkReply, вы можете проверить, завершила ли он обработку, используя isFinished(). - person Purnima; 04.05.2011
comment
@Purnima: Прошу прощения, если это звучит так, будто я использую сам оператор удаления. Я использую deleteLater() для удаления экземпляра объектов QNetworkReply. Есть ли способ остановить всю активность QNetworkReply* после того, как я вызову свой метод deleteLater() ?? - person Vishnu Pedireddi; 05.05.2011
comment
Вы можете попробовать вызвать QNetworkReply::abort() для объектов QNetworkReply, а затем выполнить операцию deleteLater(). Дополнительную информацию см. в разделе отмена. - person Purnima; 05.05.2011
comment
Спасибо за предложение, я действительно видел сбой после такого изменения. :) - person Vishnu Pedireddi; 11.05.2011

Просто идея:

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

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

person Derick Schoonbee    schedule 03.05.2011