Технически, чтобы обеспечить еще более высокую гибкость этой схемы, в приложении PyMailGUI из главы 14 с помощью этого модуля в очередь будут помещаться связанные методы — вызываемые объекты, которые, как упоминалось, хранят ссылку на функцию метода и на экземпляр объекта, что обеспечивает возможность доступа к данным объекта и другим его методам. При таком подходе программный код управления потоками выполнения в клиентском сценарии выглядит примерно так, как показано в примере 10.21 — версии реализации самотестирования из предыдущего примера, использующей классы и методы.
Пример 10.21. PP4E\Gui\Tools\threadtools-test-classes.py
# тест очереди обработчиков, но для реализации операций используются
# связанные методы
import time
from threadtools import threadChecker, startThread
from tkinter.scrolledtext import ScrolledText
class MyGUI:
def __init__(self, reps=3):
self.reps = reps # используется окно Tk по умолчанию
self.text = ScrolledText() # сохранить виджет в атрибуте
self.text.pack()
threadChecker(self.text) # запустить цикл проверки потоков self.text.bind(‘<Button-1>’, # в 3.x функция list необходима для lambda event: list(map(self.onEvent, range(6))) ) # получения всех # результатов map, для range — нет
def onEvent(self, i): # метод, запускающий поток
myname = ‘thread-%s’ % i startThread(
action = self.threadaction,
args = (i, ),
context = (myname,),
onExit = self.threadexit,
onFail = self.threadfail,
onProgress = self.threadprogress)
# основная операция, выполняемая потоком
def threadaction(self, id, progress): # то, что делает поток
for i in range(self.reps): # доступ к данным в объекте
time.sleep(1) if progress: progress(i) # обработчик progress: в очередь
if id % 2 == 1: raise Exception # ошибочный номер: неудача
# обработчики: передаются главному потоку через очередь def threadexit(self, myname):
self.text.insert(‘end’, ‘%s\texit\n’ % myname)
self.text.see(‘end’)
def threadfail(self, exc_info, myname): # имеет доступ к данным объекта self.text.insert(‘end’, ‘%s\tfail\t%s\n’ % (myname, exc_info[0])) self.text.see(‘end’)
def threadprogress(self, count, myname): self.text.insert(‘end’, ‘%s\tprog\t%s\n’ % (myname, count)) self.text.see(‘end’)
self.text.update() # допустимо: выполняется в главном потоке
if __name__ == ‘__main__’: MyGUI().text.mainloop()
В этой версии в качестве обработчиков завершения и информирования о ходе выполнения задания, помещаемых в очередь, а также основной операции, выполняемой потоком, используются связанные методы. Как мы узнали в главе 5, благодаря тому что все потоки выполняются в пределах одного и того же процесса и в одной и той же области памяти, связанные методы ссылаются на оригинальные экземпляры объектов, а не на их копии. Это позволяет им напрямую обновлять графический интерфейс и другую информацию. Кроме того, связанные методы являются нормальными вызываемыми объектами, которые могут использоваться взамен обычных функций, поэтому нет никаких препятствий к использованию их в очередях и в потоках выполнения. Помимо всего прочего, широкие возможности совместного использования данных являются одним из основных преимуществ потоков выполнения перед процессами.
Приложение PyMailGUI в главе 14 демонстрирует более практичное применение этого модуля, где он служит базовым механизмом обслуживания событий завершения потоков и информирования о ходе выполнения задания. Там основные операции в потоках также выполняются с помощью связанных методов, что позволяет потокам и обработчикам, помещаемым ими в очередь, использовать одни и те же данные. Как мы увидим далее, действия, выполняемые обработчиками из очереди, автоматически становятся безопасными в многопоточном окружении, потому что они выполняются только в контексте главного потока. Однако другие изменения в совместно используемых объектах, производимые дочерними потоками, все еще может потребоваться синхронизировать отдельно, если они выполняются без применения очереди обработчиков и есть вероятность, что они будут перекрываться во времени. Непосредственное обновление кэша электронной почты, например, может заблокировать выполнение других операций до его завершения.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011