Графические интерфейсы и потоки выполнения: предварительное знакомство

Мы еще вернемся к потокам выполнения и очередям и рассмотрим дополнительные примеры их использования, когда позже будем изучать приемы создания графических интерфейсов. В примере приложения PyMailGUI, представленном в главе 14, широко будут использоваться инструменты управления потоками выполнения и очередями, представленными здесь. В главах 10 и 9 будут обсуждаться особенности использования многопоточной модели выполнения в контексте библиотеки tkinter инструментов для построения графического интерфейса, — как только мы познакомимся с ней поближе. В этом разделе мы не будем погружаться в программный код, но отметим, что потоки выполнения обычно являются неотъемлемой частью большинства нетривиальных графических интерфейсов. Модель функционирования многих графических интерфейсов представляет собой комбинацию потоков выполнения, очередей и циклов, выполняемых по таймеру.

И вот почему. В контексте графического интерфейса любая операция, выполнение которой может быть заблокировано или занять продолжительное время, должна запускаться в параллельном потоке, чтобы графический интерфейс (главный поток) оставался активным и продолжал откликаться на действия пользователя. Подобные операции можно было бы запускать в виде отдельных процессов, однако эффективность потоков выполнения и поддерживаемая ими возможность совместного использования памяти процесса делает их идеальным инструментом для решения подобных задач. Кроме того, так как большая часть инструментов создания графических интерфейсов не позволяет обновлять интерфейс сразу из нескольких потоков, то внесение изменений в графический интерфейс будет ограничено главным потоком.

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

Если говорить более определенно:

     Главный поток выполняет все изменения в графическом интерфейсе и запускает цикл, выполняющийся по таймеру, который выполняет периодическую проверку наличия новых данных в очереди для отображения на экране. В библиотеке tkinter, например, для периодической проверки очереди можно использовать метод after(msecs, func, *args). Так как такие события распространяются процессором событий графического интерфейса, все изменения в интерфейсе будут выполняться только в главном потоке (и часто это является обязательным требованием, из-за того что инструменты создания графических интерфейсов редко поддерживают многопоточную модель выполнения).

     Дочерние потоки вообще не выполняют операций с графическим интерфейсом. Они лишь производят данные и помещают их в очередь, откуда эти данные будут извлекаться главным потоком. При необходимости дочерние потоки могут помещать в очередь функции обратного вызова, которые будут вызываться главным потоком. В программах с графическим интерфейсом обычно недостаточно просто передать функцию обратного вызова, изменяющую интерфейс, из главного потока в дочерний и вызывать ее оттуда — функция будет выполняться в дочернем потоке и, возможно, параллельно с другими потоками.

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

Кроме того, имейте в виду, что, независимо от наличия поддержки многопоточной модели выполнения в инструменте создания графического интерфейса, многопоточные программы с графическим интерфейсом по-прежнему должны придерживаться общих принципов построения многопоточных программ — может потребоваться синхронизировать доступ к общим ресурсам, если он выходит за рамки модели с очередью, совместно используемой производителями/потребителями. Если дочерние потоки должны изменять другие общие ресурсы, используемые главным потоком графического интерфейса, может потребоваться использовать блокировки, чтобы избежать их взаимовлияния. Например, дочерние потоки, загружающие и кэширующие сообщения электронной почты, не должны перекрываться по времени с другими потоками, использующими или изменяющими содержимое кэша. То есть одних только очередей может оказаться недостаточно. Если обязанность потоков не сводится к размещению своих данных в очередь, то многопоточные приложения с графическим интерфейсом должны учитывать проблемы, сопутствующие параллельной обработке данных.

Далее мы увидим, как можно реализовать многопоточную модель в графическом интерфейсе. Дополнительную информацию по этой теме вы найдете в дискуссии, посвященной использованию потоков выполнения при работе с инструментом tkinter создания графических интерфейсов в главе 9, в примерах реализации инструментов для работы с потоками выполнения и очередями в главе 10 и в примере приложения PyMailGUI в главе 14.

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

Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011

Оцените статью
Секреты программирования
Добавить комментарий