В стандартную библиотеку Python входят два модуля для работы с потоками: _thread — основной низкоуровневый интерфейс, который демонстрировался до сих пор, и threading — интерфейс более высокого уровня, основанный на объектах и классах. Внутри модуль threading использует модуль _thread для реализации объектов, представляющих потоки и инструменты синхронизации. Он в какой-то мере основан на подмножестве модели потоков выполнения языка Java, но есть различия, которые заметят только программисты Java.[XVII] В примере 5.11 приводится еще одна, последняя версия нашего сценария с потоками-счетчиками, демонстрирующая интерфейсы этого нового модуля.
Пример 5.11. PP4E\System\Threads\thread-classes.py
экземпляры класса Thread, сохраняющие информацию о состоянии и обладающие методом run() для запуска потоков выполнения; в реализации используется высокоуровневый и Java-подобный метод join класса Thread модуля threading (вместо мьютексов и глобальных переменных), чтобы известить главный родительский поток о завершении дочерних потоков; подробности о модуле threading ищите в руководстве по стандартной библиотеке;
import threading
class Mythread(threading.Thread): # подкласс класса Thread
def __init__(self, myId, count, mutex):
self.myId = myId
# информация для каждого потока
# совместно используемые объекты,
threading.Thread.__init__(self) # вместо глобальных переменных
def run(self): # run реализует логику потока
for i in range(self.count): # синхронизировать доступ к stdout
with self.mutex:
print(‘[%s] => %s’ % (self.myId, i))
stdoutmutex = threading.Lock() # то же, что и thread.allocate_lock()
threads = []
for i in range(10):
thread = Mythread(i, 100, stdoutmutex) # создать/запустить 10 потоков thread.start() # вызвать метод run потока
threads.append(thread)
for thread in threads: thread.join() # ждать завершения потока
print(‘Main thread exiting.’)
Этот сценарий производит точно такой же вывод, как и его предшественники (и снова строки случайно распределены по времени, в зависимости от используемой платформы):
C:\…\PP4E\System\Threads> python thread-classes.py
…часть вывода удалена…
[4] => 98
[8] => 97
[9] => 97
[5] => 98
[3] => 99
[6] => 98
[7] => 98
[4] => 99
[8] => 98
[9] => 98
[5] => 99
[6] => 99
[7] => 99
[8] => 99
[9] => 99
Main thread exiting.
Использование модуля threading заключается в основном в определении новых классов. Потоки в этом модуле реализуются с помощью объекта Thread — класса Python, который наследуется и специализируется в каждом приложении путем реализации метода run, определяющего действия, выполняемые потоком. Например, в данном сценарии создается подкласс Mythread класса Thread, метод run которого будет вызываться родительским классом Thread в новом потоке после создания экземпляра класса Mythread и вызова его метода start.
Иными словами, этот сценарий просто обеспечивает методы, предполагаемые структурой класса Thread. Преимущество этого приема, требующего создания большего объема программного кода, заключается в том, что он обеспечивает «бесплатный» доступ к информации о состоянии каждого потока в отдельности (в виде атрибутов экземпляра) и к ряду дополнительных инструментов для работы с потоками, предоставляемых данной структурой. К примеру, используемый в конце сценария метод Thread.join ожидает завершения (по умолчанию) потока выполнения — этот метод можно использовать, чтобы предотвратить завершение главного потока до того, как завершится дочерний поток, и отказаться от вызова функции time.sleep, глобальных блокировок и переменных, использовавшихся в предыдущих примерах с потоками.
Кроме того, для синхронизации доступа к стандартному потоку вывода в примере 5.11 используется конструктор threading.Lock (хотя в текущей реализации это просто синоним конструктора _thread.allocate_lock). Модуль threading предоставляет и другие структуры классов, но они не влияют на общую картину многопоточной модели параллельной обработки данных.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011