Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: DarkHobbit от Март 31, 2020, 20:14



Название: QEventLoop и потоки
Отправлено: DarkHobbit от Март 31, 2020, 20:14
Доброго времени суток.

Я обрабатываю массивы данных, поступающие периодическими блоками. Обработка довольно "тяжёлая", стоит проблема быстродействия. Но каждый блок состоит из нескольких подблоков, которые можно обрабатывать независимо. Я решил этим воспользоваться и распараллелить вычисления.

Есть класс-обработчик. Есть класс-менеджер, который создаёт несколько обработчиков, и каждый из них перемещает (moveToThread) в отдельную QThread. По поступлении блока менеджер рассылает обработчикам сигналы начать обработку. По окончании обработки обработчики шлют сигналы об окончании.

Менеджер в начале работы создал объект loop класса QEventLoop. У него есть функция process(), которая вызывается по получении блока. Разослав обработчикам сигналы начинать, process() вызывает loop.exec(). Слот менеджера, принимающий сигналы об окончании обработки, проверяет, все ли обработчики "отписались", и если да - вызывает loop.exit(). После этого process() возвращает управление вызвавшей программе.

Экспериментальная проверка показала следующее.

Случай 1. Менеджер находится в основном потоке GUI-программы. Всё работает прекрасно, замеры показывают, что на 4-ядерном процессоре время обработки уменьшается в 3 с небольшим раза, что полностью согласуется с так называемым здравым смыслом. Здесь вопросов нет.

Случай 2, тяжёлый. Менеджер находится внутри DLL, которую вызывает внешняя программа. Причём поскольку у внешней программы есть чем заняться и без моей DLL, вызов происходит уже НЕ в основном потоке. Выхода из loop не происходит. Отладка показывает, что exit() в слоте вызывается, но выхода из exec() в process() я не вижу.

Я пока склонен винить вызывающую программу, но претензию сформулировать толком не могу. Вообще, может ли работать описанная схема, если QEventLoop организован не в главном потоке программы? Какие ещё подводные камни с QEventLoop возможны?

P.S. Да, есть ещё QtConcurrent, который, похоже, специально создан для таких вещей, но прежде чем хвататься за него, хотелось бы понять, почему не работает "низкоуровневый" способ. А то мне интуиция подсказывает, что если результат так разительно зависит от "внешних" факторов, то и QtConcurrent может не спасти.

P.P.S Проверено на Qt 4.7 и 4.8 под Windows 7 и Debian Stretch соответственно. Да, "энтерпрайз-старьё". Призывы мигрировать на Qt5 рассматриваются, но только в том случае, если у вас есть реальный опыт с отличием работы QEventLoop на разных мажорных версиях, а не просто "все нормальные пацаны сидят на последней версии".


Название: Re: QEventLoop и потоки
Отправлено: qate от Март 31, 2020, 21:21
думаю минимальный рабочий пример помог бы разобраться в проблеме


Название: Re: QEventLoop и потоки
Отправлено: DarkHobbit от Апрель 01, 2020, 10:53
qate, я в курсе. Но вот в данном случае тяжеловато его сделать, такой пример. Особенно с учётом того, как раз в "минимальных" условиях всё прекрасно работает...


Название: Re: QEventLoop и потоки
Отправлено: qate от Апрель 01, 2020, 18:29
qate, я в курсе. Но вот в данном случае тяжеловато его сделать, такой пример. Особенно с учётом того, как раз в "минимальных" условиях всё прекрасно работает...

а в чем сложность - вместо тяжелой работы sleep поставить, не ?


Название: Re: QEventLoop и потоки
Отправлено: Igors от Апрель 02, 2020, 11:24
Отладка показывает, что exit() в слоте вызывается, но выхода из exec() в process() я не вижу.
Не берусь утверждать что "именно так" (для не главной нитки), но "вполне возможно". Поковырявшись в отладчике можно сказать точно, но не лучше ли обойтись без "своего" EventLoop? Вторичный луп всегда добавит проблем, пусть решаемых. Напр создать "нитку упр-я" и поместить Ваш process в ее run. Или даже точнее - если текущая нитка не главная, то просто запустить process. Да, нитка будет ждать пока все отработает, но, как я понял, это устраивает.

A QtConcurrent работать должен. Как и QThreadPool который мне нравится больше.


Название: Re: QEventLoop и потоки
Отправлено: DarkHobbit от Апрель 03, 2020, 12:06
QThreadPool который мне нравится больше.
Почему, кстати, если не секрет?


Название: Re: QEventLoop и потоки
Отправлено: Авварон от Апрель 03, 2020, 12:22
Почему, кстати, если не секрет?

Потому что Igors любит голые указатели и не любит всё что сложнее голого указателя=)


Название: Re: QEventLoop и потоки
Отправлено: DarkHobbit от Апрель 03, 2020, 21:17
а в чем сложность - вместо тяжелой работы sleep поставить, не ?
sleep() вместо организации QEventLoop? Его ж, как я понимаю, принудительно не разбудишь? Отвёл ему 200 миллисекунд - он все 200 и будет ждать?
Нет, можно пойти по такому пути. Но тогда надо очень тщательно выбирать время усыпления. И либо process() будет на себя отнимать драгоценное процессорное время, либо часть блоков будет просто теряться. А время обработки подблока, к сожалению, плавает в зависимости от содержимого самого подблока...


Название: Re: QEventLoop и потоки
Отправлено: qate от Апрель 04, 2020, 09:46
sleep() для имитации "тяжелой нагрузки"


Название: Re: QEventLoop и потоки
Отправлено: Igors от Апрель 04, 2020, 11:05
Почему, кстати, если не секрет?
Выглядит более естественно

Потому что Igors любит голые указатели и не любит всё что сложнее голого указателя=)
Ну в общем да

Возвращаясь к теме. Вы создаете вторичный EventLoop, не то чтобы это "преступление", но не вызывается необходимостью, а значит надо признать что "велик" (костыль и.т.п). Ну и получаете проблемы которые хотите решать. Не вижу что мешает делать Ваш process прямо в вызывающей нитке. Раздача заданий и получение ответов - операции не трудоемкие. Если это не так - создать еще нитку упр-я


Название: Re: QEventLoop и потоки
Отправлено: DarkHobbit от Июнь 09, 2020, 14:24
В общем, мне очень стыдно, но загвоздка была банально во флаге, взводящемся в неправильном месте. Просто на вариант 1 это не влияло, а во 2 варианте события об окончании обработки начинали приходить ещё до того, как основной поток осознал, что эта самая обработка началась.

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