Разделяемая память и глобальные объекты

razdelyaemaya pamyat i globalnye obekty Системные инструменты параллельного выполнения

В примере 5.31 используется разделяемая память, которая служит вводом и выводом порожденных процессов. Чтобы обеспечить переносимость этого приема, необходимо создать объекты с помощью пакета и передать их конструктору Process. Последний тест в этом примере loop 4») представляет, пожалуй, наиболее типичный случай использования разделяемой памяти — распределение заданий по нескольким параллельно выполняющимся процессам.

Пример 5.31. PP4E\System\Processes\multi3.py

Реализует взаимодействие с помощью объектов разделяемой памяти из пакета.

В Windows передаваемые объекты используются совместно, а глобальные объекты

— нет. Последняя проверка здесь отражает типичный случай использования: распределение заданий между процессами.

import os

from multiprocessing import Process, Value, Array

procs = 3 # глобальные переменные, отдельные для каждого процесса,

count = 0 # не являются совместно используемыми

def showdata(label, val, arr):

выводит значения данных в этом процессе

msg = ‘%-12s: pid:%4s, global:%s, value:%s, array:%s’

print(msg % (label, os.getpid(), count, val.value, list(arr)))

def updater(val, arr):

обменивается данными через разделяемую память

global count

count += 1 # глобальный счетчик недоступен другим процессам

val.value += 1 # а передаваемый в объекте — доступен

for i in range(3): arr[i] += 1

if __name__ == ‘__main__’:

scalar = Value(‘i’, 0) # разделяемая память: предусматривает

# синхронизацию процессов/потоков

vector = Array(‘d’, procs) # коды типов из ctypes: int, double

#   вывести начальные значения в родительском процессе

showdata(‘parent start’, scalar, vector)

#   породить дочерний процесс, передать данные в разделяемой памяти

p = Process(target=showdata, args=(‘child ‘, scalar, vector))

p.start(); p.join()

#   изменить значения в родителе и передать через разделяемую память,

#   ждать завершения каждого потомка

#   все потомки видят изменения, выполненные в родительском процессе и

#   переданные в виде аргументов (но не в глобальной памяти)

print(‘\nloop1 (updates in parent, serial children)…’)

for i in range(procs):

count += 1

scalar.value += 1

vector[i] += 1

p = Process(target=showdata, args=((‘process %s’ % i), scalar, vector))

p.start(); p.join()

#   то же самое, но потомки запускаются параллельно

#   все они видят результат последней итерации, потому что они хранятся

#   в совместно используемых объектах

print(‘\nloop2 (updates in parent, parallel children)…’)

ps = []

for i in range(procs):

count += 1

scalar.value += 1

vector[i] += 1

p = Process(target=showdata, args=((‘process %s’ % i),

scalar, vector))

p.start()

ps.append(p)

for p in ps: p.join()

#   объекты в разделяемой памяти изменяются потомками,

#   ждать завершения каждого из них

print(‘\nloop3 (updates in serial children)…’) for i in range(procs):

p = Process(target=updater, args=(scalar, vector)) p.start() p.join() showdata(‘parent temp’, scalar, vector)

#  то же самое, но потомки запускаются параллельно

ps = []

print(‘\nloop4 (updates in parallel children)…’) for i in range(procs):

p = Process(target=updater, args=(scalar, vector)) p.start() ps.append(p) for p in ps: p.join()

#  глобальная переменная count=6 доступна только родителю

#  выведет последние результаты # scalar=12: +6 в родителе, +6 в 6 потомках showdata(‘parent end’, scalar, vector) # array[i]=8:

# +2 в родителе, +6 в 6 потомках

Ниже приводится вывод этого сценария, запущенного в Windows. Проследите, как выполняется этот программный код. Обратите внимание, что глобальная переменная недоступна дочерним процессам для совместного использования в Windows, а переданные им объекты Value и Array — доступны. Последняя строка в этом выводе отражает изменения, выполненные в разделяемой памяти родительским и дочерними процессами, — все элементы в массиве имеют значение 8.0, потому что все они дважды увеличивались в родительском процессе и по одному разу — в каждом из шести дочерних процессов. Скалярное значение также отражает изменения, выполненные в родительском и в дочерних процессах, но, в отличие от потоков выполнения, в Windows глобальные переменные доступны только вмещающему их процессу:

C:\\PP4E\System\Processes> multi3.py

parent start: pid:6204, global:0, value:0, array:[0.0, 0.0, 0.0]

child : pid:9660, global:0, value:0, array:[0.0, 0.0, 0.0]

loop1 (updates in parent, serial children)

process 0 : pid:3900, global:0, value:1, array:[1.0, 0.0, 0.0]

process 1 : pid:5072, global:0, value:2, array:[1.0, 1.0, 0.0]

process 2 : pid:9472, global:0, value:3, array:[1.0, 1.0, 1.0]

loop2 (updates in parent, parallel children)

process 1 : pid:9468, global:0, value:6, array:[2.0, 2.0, 2.0]

process 2 : pid:9036, global:0, value:6, array:[2.0, 2.0, 2.0]

process 0 : pid:9548, global:0, value:6, array:[2.0, 2.0, 2.0]

loop3 (updates in serial children)

parent temp : pid:6204, global:6, value:9, array:[5.0, 5.0, 5.0]

loop4 (updates in parallel children)

parent end : pid:6204, global:6, value:12, array:[8.0, 8.0, 8.0]

А теперь представьте, что в последнем тесте используется намного больший массив и запускается большее количество дочерних процессов, — тогда вы начнете понимать, какие возможности предоставляет этот пакет для реализации параллельной обработки данных.

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

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