Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: __Heaven__ от Апрель 26, 2018, 09:45



Название: Помогите добавить отображение прогресс диалога
Отправлено: __Heaven__ от Апрель 26, 2018, 09:45
Привет, друзья!
Прошу помочь корректно решить задачу по добавлению прогресс диалога. Отображение самого прогреса операции не интересует. Хочу только антифриз окна.
Сейчас имеется такая иерархия:
MainWindow
|=GLWidget
|=MyModel
    |=MyObject - объект большого размера

Модель предоставляет наружу указатель на объект
Изменения объекта и модели испускают сигналы, которые связаны с GL опцией direct.
MyModel::load(fileName) выглядит примерно так:
Код:
parser = selectParser();
try {
prepareOperations();
model.Load(fileName);
}
catch(const FileOpenEx &)
{}
gl->setDefaultView();

Model::Load:
Код:
prepare();
myObj.clear();
myObj = parser->parse(fileName); // emit signals on assign
emit ...
emit ...

Наивная передача Model::Load в поток через QtConcurent::run выливается в необновление GL и MainWindow. Да и результат он вызывает по значению, что для меня расточительно по использованию памяти
Прошу поделиться мыслями, какое архитектурное решение здесь лучше применить.
Пока вижу, что нужно как-то в главном потоке чистить объект, далее отображать прогресс и запускать парсер, который будет отправляться в поток, а по завершению делать перемещающее присваивание полученного объекта пустому.
В таком способе не вижу, как оповестить основной поток о том, что операция чтения окончена


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: ViTech от Апрель 26, 2018, 15:14
Пока вижу, что нужно как-то в главном потоке чистить объект, далее отображать прогресс и запускать парсер, который будет отправляться в поток, а по завершению делать перемещающее присваивание полученного объекта пустому.
В таком способе не вижу, как оповестить основной поток о том, что операция чтения окончена

Сигнал о завершении чтения есть или как? Буду предполагать, что есть. Тогда, как вариант, метод Model::Load можно разделить на две части: первая выполняет подготовительные действия и отправляет в поток парсер, после этого завершается. Вторая часть оформлена слотом, который получает результат загрузки.

Ещё может как-то QFutureWatcher поможет.


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: Igors от Апрель 27, 2018, 10:08
Хочу только антифриз окна.
Обычно никакой активности юзера нельзя допускать пока идет загрузка. Поэтому просто модальный QProgressDialog, он сам вызовет processEvents. Ну конечно не забывать апдейтить диалог из загрузчика. "Вынос в поток" здесь не нужен.

Тут правда одна бяка: в процессе загрузки рисование окон может ссылаться на данные что уже разрушены. Пресекается с помощью setUpdatesEnabled(false), хотя и с приключениями (недавно там ковырялся).


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: __Heaven__ от Май 01, 2018, 14:41
Сигнал о завершении чтения есть или как? Буду предполагать, что есть. Тогда, как вариант, метод Model::Load можно разделить на две части: первая выполняет подготовительные действия и отправляет в поток парсер, после этого завершается. Вторая часть оформлена слотом, который получает результат загрузки.

Ещё может как-то QFutureWatcher поможет.
Про вотчер я и забыл. Реализовал на нём.
StartLoading запускает процесс, футуру, которого я передаю вотчеру, далее вотчер выполняет FinishLoading.
Есть вопрос. У меня может случиться FinishLoading до завершения StartLoading или вообще не отработать завершение? Например, если потоку не особо много что нужно делать

Столкнулся ещё с небольшой проблемой. Почему-то нельзя наполнять данными QOpenGLBuffer... Видимо, какие-то тонкости связанные с контекстом...

Обычно никакой активности юзера нельзя допускать пока идет загрузка. Поэтому просто модальный QProgressDialog, он сам вызовет processEvents. Ну конечно не забывать апдейтить диалог из загрузчика. "Вынос в поток" здесь не нужен.

Не, не хотелось такой связки. У меня предполагается несколько парсеров, возможно в будущем и внешние появятся. Придётся в каждом помимо парсинга заниматься и обновлением окна


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: ViTech от Май 02, 2018, 12:04
StartLoading запускает процесс, футуру, которого я передаю вотчеру, далее вотчер выполняет FinishLoading.
Есть вопрос. У меня может случиться FinishLoading до завершения StartLoading или вообще не отработать завершение? Например, если потоку не особо много что нужно делать

Параллельный поток с загрузкой вполне может завершиться до конца выполнения StartLoading, такую возможность отбрасывать нельзя. Другое дело, как, куда и когда вернётся результат загрузки. Сигнал QFutureWatcher::finished вполне можно соединить со слотом в вызывающем потоке по Qt::QueuedConnection, тогда StartLoading завершится и результат загрузки будет обработан в порядке очереди (сообщений). Вроде как-то так, надо на практике проверять :).


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: Igors от Май 02, 2018, 15:12
У меня предполагается несколько парсеров, возможно в будущем и внешние появятся. Придётся в каждом помимо парсинга заниматься и обновлением окна
Так или иначе придется если хотите отображать ход загрузки(ок). Иногда удается проскочить привязавшись к сколько % считано из файла.

Ну конечно конкретными окнами загрузчик не занимается, он обычно вызывает callback или испускает сигнал, а там уже обновят. Вообще не понял причем тут QtConcurrent если никакого параллелизма не требуется. Хотите непременно "в др потоке" (круто) - ну хорошо, тогда просто заведите рабочую QThread, создайте пресловутого worker'a и пульните его сигналом. Или QThreadPool, там еще меньше писать. В любом случае главная нитка свободна и проблема замерзания не стоит.


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: __Heaven__ от Май 03, 2018, 07:43
Вроде как-то так, надо на практике проверять :).
Я рассуждал, что QFutureWatcher занимается опросом QFuture во время проворачивания EventLoop. Таким образом получается, что EventLoop заблокирован на момент выполнения  StartLoading. Беспокойство вызывает момент присваивания результата run в QFuture и передача её в QFutureWatcher - не выполнился ли поток уже, иначе ж не стоит ожидать смены статуса QFuture.

Igors, пробовал на processEvents выполнять обновление интерфейса - остались неприятные впечатления, связанные с уменьшением скорости выполнения вычислений. Не хочется возвращаться к этому варианту. С QThread мало работал, с QThreadPool вообще не работал. QThread, наверняка, потребует больше строк кода чем QtConcurrent::run. Пока не требуется отображать прогресс.


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: Igors от Май 04, 2018, 07:10
Igors, пробовал на processEvents выполнять обновление интерфейса - остались неприятные впечатления, связанные с уменьшением скорости выполнения вычислений. Не хочется возвращаться к этому варианту. 
Возможно Вы столкнулись с типовой проблемой - индикатор дергается слишком часто, в результате "работает на индикатор" (а не на расчеты). При выносе в поток это менее заметно, но решать все равно придется.

Пока не требуется отображать прогресс.
А как же тогда название темы  ???  :)

Неясно что Вы хотите обновлять. Модель грузится целиком, для нее создаются всякие внутренние структуры данных (хотя бы буфера рисования), поэтому что там обновлять в ходе загрузки - хз. Целиком загрузили, потом целиком отобразили - нормальная, стандартная практика. 


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: Old от Май 04, 2018, 07:42
Я рассуждал, что QFutureWatcher занимается опросом QFuture во время проворачивания EventLoop.
Нет. Скорее всего этот механизм сделан на условных переменных.

Таким образом получается, что EventLoop заблокирован на момент выполнения  StartLoading.
Ничего не блокируется и весело крутится. :)

Беспокойство вызывает момент присваивания результата run в QFuture и передача её в QFutureWatcher - не выполнился ли поток уже, иначе ж не стоит ожидать смены статуса QFuture.
Нужно проверить, но скорее всего при помещении в вотчер уже завершенной задачи сигнал finished будет послан сразу. По крайней мере, это было бы логичным.


Название: Re: Помогите добавить отображение прогресс диалога
Отправлено: __Heaven__ от Май 17, 2018, 18:39
Ничего не блокируется и весело крутится. :)
StartLoading ведь в том же потоке, где и processEvents (a.exe()). До веселья ещё одна }  :)

Нужно проверить, но скорее всего при помещении в вотчер уже завершенной задачи сигнал finished будет послан сразу. По крайней мере, это было бы логичным.
Проверил. Под linux на Qt5.10. При return a.exec()  выводится два сообщения,  при return 0  - одно, но если вызвать a.processEvents(), то 2
Сопутствующий вопрос появился: почему qDebug()<< "msg" в этом примере не работает? Подскажите, пожалуйста.
Код
C++ (Qt)
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrent>
#include <iostream>
 
using namespace std;
 
void f() {
   cout << "ololo" << endl;
}
void d() {
   cout << "done" << endl;
}
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   auto future = QtConcurrent::run(f);
   QFutureWatcher<void> watcher;
   future.waitForFinished();
   QObject::connect(&watcher, &QFutureWatcher<void>::finished, []{d();});
   watcher.setFuture(future);
 
   return a.exec();
}