Форум программистов, компьютерный форум, киберфорум
iamvic
Войти
Регистрация
Восстановить пароль
Путевые заметки в процессе познания Python и PyQt/PySide.
Оценить эту запись

Иллюстрация к вопросу о многопоточности в PyQt5.

Запись от iamvic размещена 01.01.2021 в 19:04
Обновил(-а) iamvic 29.01.2021 в 12:13 (Обновление приложения)

И всё-таки как-то очень долго я примеривался к тому, о чём говорится в https://evileg.com/ru/post/579/.
Очень хороший пример, но рамки поставленной там задачи не дают возможности полностью раскрыть
всю суть проблемы.

Ведь в документации https://doc.qt.io/qt-5/threads-technologies.html говорится буквально следующее:

- что перемещение объекта-исполнителя в дополнительный поток применяется в том случае,
если требуется по запросу неоднократно выполнять различные задачи в этом дополнительном потоке
и/или передавать им различные данные для обработки,

- что жить этот дополнительный поток должен постоянно ("permanent", в терминах документации,
судя по всему, от старта приложения до его завершения),

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

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

Всё хотелось разобраться - а как со всем этим управляться-то? Что можно делать? Что нельзя?
Какие заморочки вылезают? И прочее...

И всё как-то было недосуг, а тут, на Новый Год, как раз сошлись и место, и время, и желание, и возможности...
В результате наваялась, как оказалось, презабавнейшая поделка.
Очень интересно с ней было поиграться - узнал много нового.

Что там сделано:

- дополнительный поток поднят прямо в __main__. По-моему, там ему самое место с таким временем жизни.

- в поток перемещаются два объекта-исполнителя. А что мешает? В одном собраны задачи, которые рано или
поздно сами завершатся, а в другом — те, которые без вмешательства пользователя будут длиться вечно.
Сделано это с прицелом на то, чтобы можно было убедиться, что очереди обслуживают поток в целом,
а не отдельные объекты, живущие в нём,

- всё управление - в ручном режиме из меню главного окна приложения:
получение сведений о дополнительном потоке, его запуск, завершение и терминирование,
установка запроса на прерывание, запуск тестов и завершение работы приложения,

- всё происходящее (какая задача выполняется, какие сигналы отправлены, какие получены,
кто отправитель, в каком потоке он живёт и прочая) должным образом протоколируется и в режиме
"нажми на кнопку - получишь результат" отображается в центральном виджете главного окна приложения.

- по выходу из приложения весь собранный протокол дописывается в файл threadprobe.log домашнего каталога.

Записи протокола содержат некое описание произошедшего, включая имя объекта с именем метода/слота,
сгенерировашего запись, 16-ричный идентификатор этого объекта (в круглых скобках), 16-ричный идентификатор
потока, в котором живёт этот объект (в угловых скобках) и, возможно, 16-ричный идентификатор потока,
управляющего дополнительным потоком (в обрамлении эвёздочек).

Объект дополнительного потока и объекты, живущие в дополнительном потоке, формируя нагрузку,
отправляемую сигналами, помечают начало каждой строки символами --->, что позволяет отличить их в протоколе
от остальных записей. Этой же цели служит и цветовая дифференциация в окне просмотра протокола.

Пример протокола:
Код:
<0x22ea120> mainWin.to_start_thread() reported 01/01/2021 13:41:04
startSignal emitted
<0x22ea120> mainWin.on_check_started() reported 01/01/2021 13:41:04
testThread (0x2297a80) started
sender is testThread (0x2297a80)
<0x22ea120> mainWin.to_run_short() reported 01/01/2021 13:41:09
shortSignal emitted
<0x22ea120> mainWin.on_check_test() reported 01/01/2021 13:41:09
---> <0x2297a80> testFinite.shortTest() reported 01/01/2021 13:41:09
---> testFinite.shortTest() started
sender is testFinite (0x2297ad0)
<0x22ea120> mainWin.on_check_test() reported 01/01/2021 13:41:14
---> <0x2297a80> testFinite.shortTest() reported 01/01/2021 13:41:14
---> testFinite.shortTest() done
sender is testFinite (0x2297ad0)
Сравнение идентификатора объекта testThread (строка 4) и идентификатора потока, в котором живёт
объект testFinite (строки 9 и 13), позволяет говорить о том, что задание выполнялось именно
в дополнительном потоке.

Что нового узнал, пока гонял эту программку на разной технике под разными системами:

- можно завершить дополнительный поток, а потом повторно стартовать его,

- можно натолкать заданий в очередь нестартовавшего или уже завершённого дополнительного потока.
Они начнут выполняться после старта/рестарта потока в порядке очерёдности,

- завершение дополнительного потока обычно происходит после завершения текущего задания.
Однако, если на момент старта/рестарта потока в очереди уже присутствовали задания, то поток завершится
только после выполнения всех этих заданий,

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

- терминирование сопровождается выдачей сообщения об ошибке.
Linux отплёвывается текстом в stdout сразу при выполнении QThread.terminate() и более никак себя не проявляет,
Windows терпит либо до завершения дополнительного потока после рестарта и тогда, выдав сообщение, сносит
приложение, либо до закрытия приложения и тогда выдаёт ошибку уже после закрытия окна приложения,

- можно ещё запустить приложение с параметром (параметр любой, лишь бы он был).
Тогда в терминальную сессию вывалится печать контрольных точек.
Можно понаблюдать, как GUI скачет из одного потока в другой в процессе инициализации.
Это к вопросу определения GUI-потока

Вроде, всё. С Новым Годом!

(исходник в прилагаемом архиве)
(см. обновление https://www.cyberforum.ru/blog... g6949.html)
Вложения
Тип файла: zip qthread_probe.zip (6.4 Кб, 1960 просмотров)
Размещено в Памятка
Показов 3512 Комментарии 6
Всего комментариев 6
Комментарии
  1. Старый комментарий
    Аватар для Avazart
    Цитата:
    - что перемещение объекта-исполнителя в дополнительный поток применяется в том случае,
    если требуется по запросу неоднократно выполнять различные задачи в этом дополнительном потоке
    и/или передавать им различные данные для обработки,
    Нет. Думаю так не стоит воспринимать QThread.
    Стоит воспринимать как есть т.е. как должное, что есть такой интерфейс который подразумевает сам "класс потока" и "класс рабочего" (worker).
    Для разные данных можно создавать разные объекты а не использовать одно и тоже, да и есть для этого другие средства.

    Кроме того актуален вопрос для чего Вам потоки?
    Более того вопрос усложняется спецификой языка Python так как там потоки будут блокироваться GIL и будет работать не совсем так как для С++.


    Цитата:
    Всё хотелось разобраться - а как со всем этим управляться-то? Что можно делать? Что нельзя?
    Какие заморочки вылезают? И прочее...
    Для начала стоит разобраться что такое сигнал/слот и что такое цикл обработки событий (event loop).
    Как это то с чего стоит начинать изучение Qt и что жизненно важно для понимания того как работают GUI приложения.

    Цитата:
    (исходник в прилагаемом архиве)
    Исходники стоит размещать на github а не архивом на форуме.
    Запись от Avazart размещена 03.01.2021 в 18:46 Avazart на форуме
    Обновил(-а) Avazart 03.01.2021 в 19:06
  2. Старый комментарий
    Avazart, искреннее спасибо за участие! Мне, действительно, ещё пахать и пахать

    Но, по крайней мере, даже не написание программки, а игры с этим получившимся тренажёрчиком
    помогли мне понять о чём всё-таки идёт речь в следующих статьях и, самое главное,
    в комментариях к ним:

    https://www.qt.io/blog/2010/06... g-it-wrong
    https://woboq.com/blog/qthread... wrong.html
    http://blog.debao.me/2013/08/h... ay-part-1/
    http://blog.debao.me/2013/08/h... ay-part-2/

    Всё, о чём там говорится, теперь для меня не пустой звук. после того, как я на тренажёре
    наступил на все эти грабли.

    А до размещения на github я ещё и не дорос пока Да и тут всё-таки блог, хотя и на форуме.
    Это же не портит форум. И ответственные лица не возражают.
    Запись от iamvic размещена 04.01.2021 в 10:59 iamvic вне форума
  3. Старый комментарий
    Аватар для Avazart
    Цитата:
    А до размещения на github я ещё и не дорос пока
    Гит сразу нужно учить. Если не доросли идите в садик потом в школу ... потом программировать.


    В первой статьи говориться что использовать moveToThread(self); не верно т.е. помещать поток в самого себя.
    Эту статью можно вовсе не читать. Стоит сразу смотреть примеры из официальной документации и тогда таких ошибок вовсе не будет.

    Остальные стать про то что опять же стоит отдавать предпочтению QThread + Worker класс перед прямым наследованием от QThread.
    При чем староватые статьи.

    В общем случае стоит руководствоваться правилом: в любой непонятной ситуации сначала смотри в официальную документацию, к тому же у Qt она очень хорошего качества (сравнительно) проблема лишь в том что там С++ а не Python.

    Цитата:
    а игры с этим получившимся тренажёрчиком
    Игры возможно стоит писать лучше используя PyGame, а не PyQt.
    PyQt это все же GUI программы.
    Запись от Avazart размещена 04.01.2021 в 18:57 Avazart на форуме
    Обновил(-а) Avazart 04.01.2021 в 19:02
  4. Старый комментарий
    Цитата:
    Сообщение от Avazart Просмотреть комментарий
    идите в садик потом в школу ... потом программировать.
    Avazart, вы отстали от жизни!

    В нашей младшей группе детского сада уже все - программисты. И все - на удалёнке,
    можно даже сказать, фрилансеры
    И, в силу присущей возрасту конкретности мышления, хотелось бы услышать критику и обсуждение
    именно тех решений, которые использованы в прилагаемой программе, а не общевдохновляющие лозунги
    типа "иди тудо - незнамо кудо, учи то - незнамо что".

    Если у вас есть замечания по тексту программы, то не стесняйсь - ткните пальцем в проблемное место
    и доходчиво объясните почему. Ваше замечание будет с благодарностью принято.
    Если у вас, кроме педагогического зуда, ничего нет, то забейте, не чешите, проходите мимо.
    Можете ещё оценочку выставить и нажать кнопочку "пожаловаться". Тогда, вероятно, группа специально
    обученных модераторов может принять решение вырезать этот блог.

    Правила не запрещают вам чувствовать себя в чужом блоге, как дома, но забывать о том, что вы в гостях,
    наверное, не следует? Всё-таки есть тонкие различия между форумом и блогом. У вас же опыта больше,
    вы же это наверняка понимаете?
    Запись от iamvic размещена 04.01.2021 в 23:13 iamvic вне форума
  5. Старый комментарий
    Аватар для Avazart
    Цитата:
    Если у вас есть замечания по тексту программы,
    Я не умею смотреть исходники с архивов
    Запись от Avazart размещена 05.01.2021 в 12:57 Avazart на форуме
    Обновил(-а) Avazart 05.01.2021 в 12:59
  6. Старый комментарий
    Аватар для Avazart
    ...
    Запись от Avazart размещена 05.01.2021 в 12:59 Avazart на форуме
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.