Использование потоков выполнения в графических интерфейсах tkinter

ispolzovanie potokov vypolneniya v graficheskih interfejsah tkinter Экскурсия по tkinter, часть 2

Имейте в виду, что во многих программах поддержка потоков выполнения в Python, с которой мы познакомились в главе 5, способна отчасти решать те же задачи, что и инструменты tkinter, перечисленные в предыдущем разделе, и даже позволяет использовать их. Например, чтобы избежать блокировки интерфейса (и не заставлять пользователей бездействовать) во время продолжительных операций обмена данными через файлы или сокеты, этот обмен можно выполнять в дочерних потоках выполнения, при этом остальная часть программы будет выполняться, как обычно. Аналогично графические интерфейсы, ожидающие появления данных в канале или в сокете, могут использовать для этих целей потоки выполнения, обработчики, устанавливаемые методом after, или их комбинации, и тем самым избежать блокирования графического интерфейса.

Однако при использовании потоков выполнения в программах на основе библиотеки tkinter обращения к графическому интерфейсу должны выполняться только из главного потока (в котором был создан интерфейс и запущена функция mainloop). По крайней мере, потоки не должны пытаться одновременно изменять графический интерфейс. Например, метод update, описанный в предыдущем разделе, исторически является источником проблем в многопоточных графических интерфейсах — если вызывать его (или другой метод, вызывающий update) в порожденных потоках выполнения, он иногда может приводить к неожиданному и даже эффектному краху программы.

Чтобы увидеть простые и яркие примеры, демонстрирующие небезопасность обращения к графическим интерфейсам на базе tkinter из нескольких потоков выполнения, загляните в следующие сценарии, находящиеся в дереве примеров к книге, и попробуйте запустить их:

…\PP4E\Gui\Tour\threads-demoAll-frm.py

…\PP4E\Gui\Tour threads-demoAll-win.py

Эти сценарии являются версиями примеров 8.32 и 8.33 из предыдущей главы, которые конструируют четыре демонстрационных компонента графического интерфейса в параллельно выполняющихся потоках. Оба они зависают в Windows и требуют принудительного завершения. Но, хотя некоторые операции с графическим интерфейсом могут безопасно выполняться параллельно в разных потоках (например, смотрите пример 9.32, где выполняется перемещение элементов на холсте), тем не менее в целом библиотека tkinter не поддерживает многопоточную модель выполнения. (Дополнительные доказательства этого утверждения вы найдете в обсуждении многопоточной реализации циклов обновления в следующей главе, сразу после примера 10.28 — в этом примере поток выполнения пытается вывести новое окно, что вызывает сбой в работе графического интерфейса.)

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

Некоторые операции над графическим интерфейсом поддерживают возможность выполнения в многопоточном режиме, тем не менее программы с графическим интерфейсом лучше делить на главный поток, управляющий графическим интерфейсом, и ряд «рабочих» потоков, не связанных с интерфейсом, что поможет избежать потенциальных конфликтов и решить проблему поддержки многопоточности в целом. Программа PyMailGUI, представленная далее в книге, например, вызывает функции-обработчики, сохраняемые потоками выполнения в очереди.

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

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

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

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