Russian Qt Forum
Ноябрь 22, 2024, 13:44 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: [РЕШЕНО] Проблема со сложным списком  (Прочитано 4665 раз)
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« : Май 27, 2019, 23:58 »

Есть довольно жирный список, обрабатываемый на C++. В каждом элементе списка 17 строк QString, 3 картинки QPicture (правда небольших), 2 вещественных числа, 1 целое и два логических. Вся эта лабуда живёт в собственном классе, из указателей на объекты которого состоит собственно QList. В одном объекте этот список хранится и обрабатывается, другим объектом отображается при помощи QML. Список в процессе модифицируется, тогда объект хранитель передаёт объекту отображателю ссылку через сигнал-слот. Объект отображатель передёргивает контекст, и изображение списка соответствует его данным. Всё хорошо работает, пока я не начинаю удалять элементы из списка. Точнее даже - если я удаляю один элемент, то всё хорошо работает. Но если я в цикле последовательно удаляю несколько элементов подряд, разумеется через удаление одного, то всё падает. В соседней теме я это постил, но её закончил, поэтому повторю тут. Объект обработчик удаляет помеченные элементы из списка, и посылает сигнал:

Код:
void ProcessSheet::deleteSeveral()
{
    for( int i = 0; i < collection.size(); )
    {
        if( collection.at( i )->todelete() )
            delete collection.takeAt( i );
        else
            i++ ;
    }
    emit sendCollection( (QList<QObject*>&) collection );
}

Объект отображатель получает сигнал:

Код:
void DrawSheet::receiveCollection(QList<QObject*>& sheet)
{
    rootContext()->setContextProperty("CollectionList", QVariant::fromValue(sheet));
    setSource(QUrl( qmlfile ));
}

Всё прекрасно работает, пока 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() - не помогло.
« Последнее редактирование: Июнь 21, 2019, 02:05 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #1 : Май 28, 2019, 06:22 »

Код:
void ProcessSheet::deleteSeveral(int N)
{
    for( int i = 0; i < N; )
        delete collection.takeAt( i );
    emit sendCollection( (QList<QObject*>&) collection );
}
Удалять N первых элементов лучше так:
Код
C++ (Qt)
void ProcessSheet::deleteSeveral(int N)
{
   for( int i = 0; i < N; )
       delete collection.takeAt( 0 );   // <<<<<<
   emit sendCollection( (QList<QObject*>&) collection );
}

takeAt удаляет элемент из списка, значит при второй итерации тот элемент который был 1, становиться 0.
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #2 : Май 28, 2019, 11:38 »

Удалять N первых элементов лучше так:
...
takeAt удаляет элемент из списка, значит при второй итерации тот элемент который был 1, становиться 0.
На самом деле там код чуть сложнее, при его копировании и упрощении  ошибка допущена. Я его упростил здесь, чтобы не отвлекать от проблемы. Сути это не меняет. Достаточно удалить 2+ любых элемента, чтобы появилась проблема.

Я подредактировал код, сделал ближе к тому что есть на самом деле.
« Последнее редактирование: Май 28, 2019, 12:22 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #3 : Май 28, 2019, 13:15 »

О! Кажется решил!

Во-1ых, вместо
Код:
delete collection.takeAt( i );
отложенное удаление
Код:
collection.takeAt( i )->deleteLater();

Во-2ых, перед передачей сигнала:
Код:
QApplication::processEvents();
emit sendCollection( (QList<QObject*>&) collection );

И ничего, тфутфутфу, не падает, элементы удаляются, список отображается без них - и на ПК Linux, и на Anroid.  Смеющийся

Пока не буду помечать как [РЕШЕНО], позже если не вылезут ещё какие-нибудь глюки. Чтобы привлекало внимание, может кто-то выскажет ещё мысли.
Записан

2^7-1 == 127, задумайтесь...
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.243 секунд. Запросов: 23.