Ожидание завершения порожденных потоков выполнения

ozhidanie zaversheniya porozhdennyh potokov vypolneniya Системные инструменты параллельного выполнения

Помимо устранения конфликтов при выводе данных, блокировки модуля потоков имеют и другие очень полезные применения. Они могут использоваться в качестве основы парадигм синхронизации более высокого уровня (например, семафоров) и использоваться как универсальные инструменты взаимодействий между потоками.[XVI] В частности, в примере 5.8 глобальный список блокировок позволяет установить окончание работы всех дочерних потоков.

Пример 5.8. PP4E\System\Threads\thread-count-wait1.py

использование мьютексов в родительском/главном потоке выполнения для определения момента завершения дочерних потоков, взамен time.sleep; блокирует stdout, чтобы избежать конфликтов при выводе;

import _thread as thread

stdoutmutex = thread.allocate_lock()

exitmutexes = [thread.allocate_lock() for i in range(10)]

def counter(myId, count):

for i in range(count):

stdoutmutex.acquire()

print(‘[%s] => %s’ % (myId, i))

stdoutmutex.release()

exitmutexes[myId].acquire() # сигнал главному потоку

for i in range(10):

thread.start_new_thread(counter, (i, 100))

for mutex in exitmutexes:

while not mutex.locked(): pass

print(‘Main thread exiting.’)

Для проверки состояния блокировки можно использовать ее метод locked. Главный поток создает по одной блокировке для каждого дочернего потока, помещая их в глобальный список exitmutexes (не забывайте, что функция потока использует глобальную область совместно с главным потоком). По завершении каждый поток приобретает свою блокировку в списке, а главный поток просто ждет, когда будут приобретены все блокировки. Это значительно более точный подход, чем просто приостанавливать работу на определенное время, пока выполняются дочерние потоки, в надежде обнаружить после возобновления, что все они будут завершены.

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

Пример 5.9. PP4E\System\Threads\thread-count-wait2.py

использование простых глобальных данных (не мьютексов) для определения момента завершения всех потоков в родительском/главном потоке; потоки совместно используют список, но не его элементы, при этом предполагается, что после создания список не будет перемещаться в памяти

import _thread as thread

stdoutmutex = thread.allocate_lock()

exitmutexes = [False] * 10

def counter(myId, count): for i in range(count): stdoutmutex.acquire() print(‘[%s] => %s’ % (myId, i)) stdoutmutex.release()

exitmutexes[myId] = True # сигнал главному потоку

for i in range(10):

thread.start_new_thread(counter, (i, 100))

while False in exitmutexes: pass print(‘Main thread exiting.’)

Вывод этого сценария похож на вывод предыдущего — 10 потоков параллельно ведут счет до 100 и в процессе работы синхронизируют свои обращения к функции print. Фактически оба последних сценария с потоками-счетчиками производят вывод, в общем аналогичный первоначальному сценарию thread_count.py, но данные при выводе в stdout не повреждаются, значения счетчиков больше и отличается случайный порядок вывода строк. Основное отличие состоит в том, что главный поток завершает работу сразу после (и не раньше!) порожденных дочерних потоков:

C:\\PP4E\System\Threads> python thread-count-wait2.py

…часть вывода удалена…

[4]  => 98

[6]  => 98

[8]  => 98

[5]  => 98

[0] => 99

[7] => 98

[9] => 98

[1]  => 99

[3]  => 99

[2]  => 99

[4]  => 99

[6]  => 99

[8] => 99

[5]  => 99

[7] => 99

[9] => 99

Main thread exiting.

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

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