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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: QEventLoop и потоки  (Прочитано 10426 раз)
DarkHobbit
Самовар
**
Offline Offline

Сообщений: 189


Просмотр профиля
« : Март 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 на разных мажорных версиях, а не просто "все нормальные пацаны сидят на последней версии".
« Последнее редактирование: Март 31, 2020, 20:34 от DarkHobbit » Записан

Мои проекты на Qt: DoubleContact, LInvert
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #1 : Март 31, 2020, 21:21 »

думаю минимальный рабочий пример помог бы разобраться в проблеме
Записан
DarkHobbit
Самовар
**
Offline Offline

Сообщений: 189


Просмотр профиля
« Ответ #2 : Апрель 01, 2020, 10:53 »

qate, я в курсе. Но вот в данном случае тяжеловато его сделать, такой пример. Особенно с учётом того, как раз в "минимальных" условиях всё прекрасно работает...
Записан

Мои проекты на Qt: DoubleContact, LInvert
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #3 : Апрель 01, 2020, 18:29 »

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

а в чем сложность - вместо тяжелой работы sleep поставить, не ?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Апрель 02, 2020, 11:24 »

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

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

Сообщений: 189


Просмотр профиля
« Ответ #5 : Апрель 03, 2020, 12:06 »

QThreadPool который мне нравится больше.
Почему, кстати, если не секрет?
Записан

Мои проекты на Qt: DoubleContact, LInvert
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #6 : Апрель 03, 2020, 12:22 »

Почему, кстати, если не секрет?

Потому что Igors любит голые указатели и не любит всё что сложнее голого указателя=)
Записан
DarkHobbit
Самовар
**
Offline Offline

Сообщений: 189


Просмотр профиля
« Ответ #7 : Апрель 03, 2020, 21:17 »

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

Мои проекты на Qt: DoubleContact, LInvert
qate
Супер
******
Offline Offline

Сообщений: 1175


Просмотр профиля
« Ответ #8 : Апрель 04, 2020, 09:46 »

sleep() для имитации "тяжелой нагрузки"
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Апрель 04, 2020, 11:05 »

Почему, кстати, если не секрет?
Выглядит более естественно

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

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

Сообщений: 189


Просмотр профиля
« Ответ #10 : Июнь 09, 2020, 14:24 »

В общем, мне очень стыдно, но загвоздка была банально во флаге, взводящемся в неправильном месте. Просто на вариант 1 это не влияло, а во 2 варианте события об окончании обработки начинали приходить ещё до того, как основной поток осознал, что эта самая обработка началась.

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

Мои проекты на Qt: DoubleContact, LInvert
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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