Название: [РЕШЕНО] Проблема со сложным списком Отправлено: Гурман от Май 27, 2019, 23:58 Есть довольно жирный список, обрабатываемый на C++. В каждом элементе списка 17 строк QString, 3 картинки QPicture (правда небольших), 2 вещественных числа, 1 целое и два логических. Вся эта лабуда живёт в собственном классе, из указателей на объекты которого состоит собственно QList. В одном объекте этот список хранится и обрабатывается, другим объектом отображается при помощи QML. Список в процессе модифицируется, тогда объект хранитель передаёт объекту отображателю ссылку через сигнал-слот. Объект отображатель передёргивает контекст, и изображение списка соответствует его данным. Всё хорошо работает, пока я не начинаю удалять элементы из списка. Точнее даже - если я удаляю один элемент, то всё хорошо работает. Но если я в цикле последовательно удаляю несколько элементов подряд, разумеется через удаление одного, то всё падает. В соседней теме я это постил, но её закончил, поэтому повторю тут. Объект обработчик удаляет помеченные элементы из списка, и посылает сигнал:
Код: void ProcessSheet::deleteSeveral() Объект отображатель получает сигнал: Код: void DrawSheet::receiveCollection(QList<QObject*>& sheet) Всё прекрасно работает, пока N == 1. При большем числе падает глубоко внутри setContextProperty(). Падает 100% в одном и том же месте и на ПК Linux, и на смартфоне Android. При рассмотрении стека после падения, я предположил, что возникает гонка за памятью - очевидно setContextProperty() внутри выполняется в отдельном треде, и возможно этот тред обгоняет незавершённое освобождение памяти после удаления сложного объекта, что приводит к обращению по неверному указателю. Соединение sendCollection( QList<QObject*>& ) и receiveCollection( QList<QObject*>& ) производится в QtDesigner, то есть выполняется Qt::DirectConnection. Я удалил его, и попытался в коде соединить эти сигнал и слот как Qt::QueuedConnection, чтобы вызов отображения оказался в очереди, и выполнился когда все удаления будут завершены (по опыту это часто работает). К моему большому удивлению этот connect() не выполнился. Он вернул false. Если я заменил тип на Qt::DirectConnection - соединение выполняется, получаю true, но так программа падает. Подтверждение гонки тредов я получил когда соединил сигнал и слот через Qt::BlockingQueuedConnection - внутри setContextProperty() всё повисло с выдачей в консоль сообщения о dead lock. В Интернет ничего похожего не нашёл. Кто-нибудь что-нибудь читал про выполнение setContextProperty()? Может какой-нибудь недокументированный семафор есть, чтобы задержать его? Или другой способ гарантировать завершение удаления? Пробовал сразу после него QApplication::processEvents();, пробовал это же перед выполнением setContextProperty() - не помогло. Название: Re: Проблема со сложным списком Отправлено: Old от Май 28, 2019, 06:22 Код: void ProcessSheet::deleteSeveral(int N) Код
takeAt удаляет элемент из списка, значит при второй итерации тот элемент который был 1, становиться 0. Название: Re: Проблема со сложным списком Отправлено: Гурман от Май 28, 2019, 11:38 Удалять N первых элементов лучше так: На самом деле там код чуть сложнее, при его копировании и упрощении ошибка допущена. Я его упростил здесь, чтобы не отвлекать от проблемы. Сути это не меняет. Достаточно удалить 2+ любых элемента, чтобы появилась проблема.... takeAt удаляет элемент из списка, значит при второй итерации тот элемент который был 1, становиться 0. Я подредактировал код, сделал ближе к тому что есть на самом деле. Название: Re: Проблема со сложным списком Отправлено: Гурман от Май 28, 2019, 13:15 О! Кажется решил!
Во-1ых, вместо Код: delete collection.takeAt( i ); Код: collection.takeAt( i )->deleteLater(); Во-2ых, перед передачей сигнала: Код: QApplication::processEvents(); И ничего, тфутфутфу, не падает, элементы удаляются, список отображается без них - и на ПК Linux, и на Anroid. ;D Пока не буду помечать как [РЕШЕНО], позже если не вылезут ещё какие-нибудь глюки. Чтобы привлекало внимание, может кто-то выскажет ещё мысли. |