Название: 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 варианте события об окончании обработки начинали приходить ещё до того, как основной поток осознал, что эта самая обработка началась.
Приношу извинения всем, кого напряг по тупому (как выяснилось) вопросу. |