Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: vintik от Июль 30, 2013, 00:25



Название: [РЕШЕНО] QStandardItemModel QSortFilterProxyModel QThread
Отправлено: vintik от Июль 30, 2013, 00:25
Привет!
Столкнулся со следующей проблемой.
Есть вьюха и модель. Данные в модель поступают по сигналу, очень часто. Захотел засунуть модель и обработку входных сигналов в отдельный поток. Сделал это примерно так
Код:
Widget::Widget(QWidget *parent)
    : QWidget        (parent)
    , view_          (new QTableView(this))
    , data_generator_(new data_generator(this))
    , model_         (new QStandardItemModel())
    , thread_        (new QThread())
{
    view_->setModel(model_);
    connect(data_generator_, SIGNAL(data(double)), model_, SLOT(on_data(double)));
    model_->moveToThread(thread_);
    thread_->start();
}

Widget::~Widget()
{
    thread_->quit();
    thread_->wait();
    
    delete model_;
    delete thread_;
}

Это работает.
Захотелось применить прокси модель для фильтрации данных. И тут начались проблемы. Программа собирается, не ломается, но фильтрация работает криво. Если модель не мувить в поток, работает как ожидалось.
Думаю, что в данной ситуации неправильно использую прокси модель. Но где именно ошибка понять не могу.

Код:
Widget::Widget(QWidget *parent)
    : QWidget        (parent)
    , view_          (new QTableView(this))
    , data_generator_(new data_generator(this))
    , proxy_model_   (new QSortFilterProxyModel(this))
    , model_         (new QStandardItemModel())
    , thread_        (new QThread())
{
    connect(data_generator_, SIGNAL(data(double)), model_, SLOT(on_data(double)));
    proxy_model_->setSourceModel(model_);
    view_->setModel(proxy_model_);
    
    model_->moveToThread(thread_);
    thread_->start();
}

Widget::~Widget()
{
    thread_->quit();
    thread_->wait();
    
    delete model_;
    delete thread_;
}



Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: popper от Июль 31, 2013, 13:40
Предлагаю попробовать moveToThread поместить перед connect(...)


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: mutineer от Июль 31, 2013, 13:46
Предлагаю попробовать moveToThread поместить перед connect(...)
Что это должно дать?

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


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: Old от Июль 31, 2013, 15:07
Что это должно дать?
Qt определит, что объект модели должен жить в другой нитке и будет использовать QueuedConnection.


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: mutineer от Июль 31, 2013, 15:10
Что это должно дать?
Qt определит, что объект модели должен жить в другой нитке и будет использовать QueuedConnection.

Это проверяется в момент эмита сигнала, а не в момент коннекта


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: vintik от Июль 31, 2013, 15:49
Предлагаю попробовать moveToThread поместить перед connect(...)
Что это должно дать?

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

Я пробовал сделать так.
Создал класс, унаследованный от QSortFilterProxyModel - proxy_model. В этом классе мембером сделал QStandardItemModel и установил её в качестве sourceModel.
Потом, установил прокси модель во вьюху. И сделал мув прокси модели в отдельный поток.

На маленьком тестовом примере - всё заработало как надо. А когда я попытался перенести такой подход в большой проект - программа начала постоянно валиться.
Природа поломки мне осталась не ясна, но происходило это как правило в момент вставки данных в модель.
В данном проекте очень часто происходит добавление/удаление данных в модели.

Коллеги подсказали, что вообще кутешная model/view НЕ потокобезопасна. и что, видимо, нужно очень внимательно синхронизировать данные.
Дальше пока не продвинулся.. как-то так


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: mutineer от Июль 31, 2013, 16:09
Не мувай модели в треды, пусть живут в главном. Переработай схему добавления данных в модель


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: vintik от Июль 31, 2013, 17:29
Не мувай модели в треды, пусть живут в главном. Переработай схему добавления данных в модель

Расскажу из-за чего сыр-бор. Есть модель куда очень часто поступают данные. Модель сделана для отображения данных в виде дерева в QTreeView. Есть ещё прокси модель, чтобы была возможность фильтровать данные. Когда данных становится - много приложение тормозит.
Я почему-то решил, что если кухню моделей засунуть в отдельный поток, то всё наладится.
Отсюда и возник топик.

Но теперь, как я понимаю, подобная затея порочна? (т.к. порой из вьюхи происходит обращение к модели непосредственно, без сигналов/слотов  и данные могут рассинхронизироваться)
следует модель, прокси модель и вьюху содержать в одном потоке. а работу приложения ускорять различными оптимизациями?


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: mutineer от Июль 31, 2013, 17:31
Если данные поступают очень часто, то нет нужды их пихать в модель по факту прихода - все равно этого не видно будет на экране. Сделай некий буфер, в котором копи поступающие данные, а в модель их передавай раз в некоторое время, например раз в 500 мс


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: vintik от Июль 31, 2013, 17:32
Если данные поступают очень часто, то нет нужды их пихать в модель по факту прихода - все равно этого не видно будет на экране. Сделай некий буфер, в котором копи поступающие данные, а в модель их передавай раз в некоторое время, например раз в 500 мс

да, это мысль! попробую реализовать)

и всё-таки для ясности - о потоках забыть для этого случая?


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: mutineer от Июль 31, 2013, 17:35
Копить данные можно в потоке, но модель и вью должны жить в одном потоке


Название: Re: QStandardItemModel QSortFilterProxyModel QThread
Отправлено: vintik от Июль 31, 2013, 17:41
Копить данные можно в потоке, но модель и вью должны жить в одном потоке

я понял, спасибо большое!


Название: Re: [РЕШЕНО] QStandardItemModel QSortFilterProxyModel QThread
Отправлено: Sasha от Декабрь 10, 2013, 12:53
У меня тоже примерно такая же проблемма. Пошёл под отладчиком и пришёл к выводу, что причина ряда проблемм в том, что, например, прокси-модель следит за изменениями основной модели для чего коннектит сигналы основной модели к своим слотам, НО не все эти сигналы могут быть QueuedConnection. Кажется, такими не могут быть, например, имеющие в параметрах QModelIndex. Вот и получается, что сигналы не доходят. Но там, по-моему, и более серьёзные проблеммы есть, приводящие к падениям, а не просто к неправильному отображению, когда один поток использует данные, которые другой поток уже удалил.