Документация Python неофициальный перевод

multiprocessing.md

3312 строк · 166.6 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.0/library/multiprocessing.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `multiprocessing` – интерфейс «потоков» на основе процессов89## Введение1011`multiprocessing` – это пакет, поддерживающий порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading). Пакет `multiprocessing` предоставляет как локальный, так и удалённый параллелизм, обходя [*глобальную блокировку интерпретатора*](https://python-all.ru/3.0/glossary.html#term-global-interpreter-lock) за счёт использования подпроцессов вместо потоков. Благодаря этому модуль `multiprocessing` позволяет программисту полностью задействовать несколько процессоров на данной машине. Он работает как под Unix, так и под Windows.1213> **Предупреждение**14>15> Часть функционала этого пакета требует работающей реализации разделяемого семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки его импорта приведут к [`ImportError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.ImportError). Дополнительную информацию см. в [issue 3770](https://python-all.ru/3.0/library/multiprocessing.html).1617> **Примечание**18>19> Функциональность этого пакета требует, чтобы метод `__main__` был импортируем дочерними процессами. Это описано в [*Рекомендациях по программированию*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-programming), однако здесь стоит об этом упомянуть. Это означает, что некоторые примеры, такие как `multiprocessing.Pool`, не будут работать в интерактивном интерпретаторе. Например:20>21> ```python22> >>> from multiprocessing import Pool23> >>> p = Pool(5)24> >>> def f(x):25> ...     return x*x26> ...27> >>> p.map(f, [1,2,3])28> Process PoolWorker-1:29> Process PoolWorker-2:30> Traceback (most recent call last):31> AttributeError: 'module' object has no attribute 'f'32> AttributeError: 'module' object has no attribute 'f'33> AttributeError: 'module' object has no attribute 'f'34> ```3536### Класс [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process)3738В `multiprocessing` процессы порождаются созданием объекта [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.start). [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process) следует API [`threading.Thread`](https://python-all.ru/3.0/library/threading.html#threading.Thread). Простейший пример многопроцессной программы:3940```python41 from multiprocessing import Process4243def f(name):44    print('hello', name)4546 if __name__ == '__main__':47     p = Process(target=f, args=('bob',))48     p.start()49     p.join()50```5152Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:5354```python55from multiprocessing import Process56import os5758def info(title):59    print title60    print 'module name:', __name__61    print 'parent process:', os.getppid()62    print 'process id:', os.getpid()6364def f(name):65    info('function f')66    print 'hello', name6768if __name__ == '__main__':69    info('main line')70    p = Process(target=f, args=('bob',))71    p.start()72    p.join()73```7475Зачем (в Windows) нужна конструкция `if __name__ == '__main__'`, объясняется в разделе [*Рекомендации по программированию*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-programming).7677### Обмен объектами между процессами7879`multiprocessing` поддерживает два типа коммуникационных каналов между процессами:8081**Очереди**8283> Класс [`Queue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue) является почти копией [`queue.Queue`](https://python-all.ru/3.0/library/queue.html#queue.Queue). Например:84>85> ```python86> from multiprocessing import Process, Queue87>88> def f(q):89>     q.put([42, None, 'hello'])90>91> if __name__ == '__main__':92>     q = Queue()93>     p = Process(target=f, args=(q,))94>     p.start()95>     print(q.get())    # prints "[42, None, 'hello']"96>     p.join()97> ```98>99> Очереди потокобезопасны и безопасны для процессов.100101**Каналы**102103> Функция [`Pipe()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является двунаправленным. Например:104>105> ```python106> from multiprocessing import Process, Pipe107>108> def f(conn):109>     conn.send([42, None, 'hello'])110>     conn.close()111>112> if __name__ == '__main__':113>     parent_conn, child_conn = Pipe()114>     p = Process(target=f, args=(child_conn,))115>     p.start()116>     print(parent_conn.recv())   # prints "[42, None, 'hello']"117>     p.join()118> ```119>120> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы [`send()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection.send) и [`recv()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection.recv) (среди прочих). Следует учитывать, что данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.121122### Синхронизация между процессами123124`multiprocessing` содержит аналоги всех примитивов синхронизации из [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс выводит на стандартный вывод за раз:125126```python127from multiprocessing import Process, Lock128129def f(l, i):130    l.acquire()131    print('hello world', i)132    l.release()133134if __name__ == '__main__':135    lock = Lock()136137    for num in range(10):138        Process(target=f, args=(lock, num)).start()139```140141Без использования блокировки вывод от разных процессов может полностью перепутаться.142143### Совместное использование состояния между процессами144145Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.146147Однако если всё же требуется использовать общие данные, то `multiprocessing` предоставляет несколько способов сделать это.148149**Разделяемая память**150151> Данные могут храниться в карте разделяемой памяти с помощью [`Value`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Array). Например, следующий код152>153> ```python154> from multiprocessing import Process, Value, Array155>156> def f(n, a):157>     n.value = 3.1415927158>     for i in range(len(a)):159>         a[i] = -a[i]160>161> if __name__ == '__main__':162>     num = Value('d', 0.0)163>     arr = Array('i', range(10))164>165>     p = Process(target=f, args=(num, arr))166>     p.start()167>     p.join()168>169>     print(num.value)170>     print(arr[:])171> ```172>173> выведет174>175> ```python176> 3.1415927177> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]178> ```179>180> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, являются кодами типов, используемыми модулем [`array`](https://python-all.ru/3.0/library/array.html#module-array): `'d'` обозначает число двойной точности с плавающей запятой, а `'i'` – целое число со знаком. Эти разделяемые объекты будут потокобезопасны и безопасны для процессов.181>182> Для большей гибкости при использовании общей памяти можно применить модуль `multiprocessing.sharedctypes`, который поддерживает создание произвольных объектов ctypes, размещённых в общей памяти.183184**Серверный процесс**185186> Объект менеджера, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими через прокси.187>188> Менеджер, возвращаемый `Manager()`, будет поддерживать типы [`list`](https://python-all.ru/3.0/library/functions.html#list), [`dict`](https://python-all.ru/3.0/library/stdtypes.html#dict), `Namespace`, [`Lock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Event), [`Queue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Array). Например,189>190> ```python191> from multiprocessing import Process, Manager192>193> def f(d, l):194>     d[1] = '1'195>     d['2'] = 2196>     d[0.25] = None197>     l.reverse()198>199> if __name__ == '__main__':200>     manager = Manager()201>202>     d = manager.dict()203>     l = manager.list(range(10))204>205>     p = Process(target=f, args=(d, l))206>     p.start()207>     p.join()208>209>     print(d)210>     print(l)211> ```212>213> выведет214>215> ```python216> {0.25: None, 1: '1', '2': 2}217> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]218> ```219>220> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.221222### Использование пула рабочих процессов223224Класс `Pool` представляет пул рабочих процессов. Он имеет методы, которые позволяют делегировать задачи рабочим процессам несколькими различными способами.225226Например:227228```python229from multiprocessing import Pool230231def f(x):232    return x*x233234if __name__ == '__main__':235    pool = Pool(processes=4)              # start 4 worker processes236    result = pool.apply_async(f, [10])     # evaluate "f(10)" asynchronously237    print result.get(timeout=1)           # prints "100" unless your computer is *very* slow238    print pool.map(f, range(10))          # prints "[0, 1, 4,..., 81]"239```240241## Ссылка242243Пакет `multiprocessing` в основном повторяет API модуля [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading).244245### [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process) и исключения246247#### `[multiprocessing.Process]class multiprocessing.Process([group[, target[, name[, args[, kwargs]]]]])`248249Объекты Process представляют активность, которая выполняется в отдельном процессе. Класс [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process) содержит эквиваленты всех методов [`threading.Thread`](https://python-all.ru/3.0/library/threading.html#threading.Thread).250251Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должно быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/3.0/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию он равен `None`, то есть ничего не вызывается. *name* – это имя процесса. По умолчанию генерируется уникальное имя вида 'Process-N1:N2:...:Nk', где N1,N2,...,Nk – последовательность целых чисел, длина которой определяется *поколением* процесса. *args* – кортеж аргументов для вызова цели. *kwargs* – словарь именованных аргументов для вызова цели. По умолчанию в *target* не передается никаких аргументов.252253Если подкласс переопределяет конструктор, он должен убедиться, что вызывает конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.254255#### `[multiprocessing.Process.run]run()`256257Метод, представляющий активность процесса.258259Вы можете переопределить этот метод в подклассе. Стандартный метод [`run()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.260261#### `[multiprocessing.Process.start]start()`262263Запускает активность процесса.264265Этот метод должен вызываться не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.266267#### `[multiprocessing.Process.join]join([timeout])`268269Блокирует вызывающий поток до тех пор, пока процесс, для которого вызван метод [`join()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.join), не завершится или не истечет указанное время ожидания.270271Если *timeout* равен `None`, то время ожидания не ограничено.272273Процесс может быть присоединён много раз.274275Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.276277#### `[multiprocessing.Process.name]name`278279Имя процесса.280281Имя – это строка, используемая только для идентификации. Она не несет семантической нагрузки. Несколько процессов могут иметь одинаковое имя. Начальное имя задается конструктором.282283#### `[multiprocessing.Process.is_alive]is_alive()`284285Возвращает, жив ли процесс.286287Грубо говоря, объект процесса считается живым с момента возврата метода [`start()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.288289#### `[multiprocessing.Process.daemon]daemon`290291Флаг daemon процесса, логическое значение. Должен быть установлен до вызова [`start()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.start).292293Начальное значение наследуется от создающего процесса.294295Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.296297Обратите внимание, что демоническому процессу не разрешается создавать дочерние процессы. В противном случае, если демонический процесс завершится при выходе из родительского процесса, его дочерние процессы останутся бесхозными.298299В дополнение к API `Threading.Thread`, объекты [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process) также поддерживают следующие атрибуты и методы:300301#### `[multiprocessing.Process.pid]pid`302303Возвращает идентификатор процесса. До запуска процесса будет304305`None`306307.308309#### `[multiprocessing.Process.exitcode]exitcode`310311Код завершения дочернего процесса. Будет312313`None`314315, если процесс ещё не завершился. Отрицательное значение316317*-N*318319указывает, что дочерний процесс был завершён сигналом320321*N*322323.324325#### `[multiprocessing.Process.authkey]authkey`326327Ключ аутентификации процесса (байтовая строка).328329При инициализации `multiprocessing` главному процессу назначается случайная строка с помощью `os.random()`.330331При создании объекта [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process) он наследует ключ аутентификации своего родительского процесса, хотя это можно изменить, установив [`authkey`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.authkey) в другую строку байтов.332333См. [*Ключи аутентификации*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-auth-keys).334335#### `[multiprocessing.Process.terminate]terminate()`336337Завершает процесс. В Unix это делается с помощью сигнала `SIGTERM`; в Windows используется `TerminateProcess`. Обратите внимание, что обработчики выхода и блоки finally и т.д. выполнены не будут.338339Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.340341> **Предупреждение**342>343> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.344345Обратите внимание, что методы [`start()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.is_alive) и `exit_code` должны вызываться только тем процессом, который создал объект процесса.346347Пример использования некоторых методов [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process):348349```python350>>> import multiprocessing, time, signal351>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))352>>> print(p, p.is_alive())353<Process(Process-1, initial)> False354>>> p.start()355>>> print(p, p.is_alive())356<Process(Process-1, started)> True357>>> p.terminate()358>>> print(p, p.is_alive())359<Process(Process-1, stopped[SIGTERM])> False360>>> p.exitcode == -signal.SIGTERM361True362```363364#### `[multiprocessing.BufferTooShort]exception multiprocessing.BufferTooShort`365366Исключение, вызываемое [`Connection.recv_bytes_into()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection.recv_bytes_into), когда переданный объект буфера слишком мал для читаемого сообщения.367368Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде строки байтов.369370### Каналы и очереди371372При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.373374Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает множество производителей и потребителей).375376Типы [`Queue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue) и [`JoinableQueue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue) – это очереди FIFO с множеством производителей и множеством потребителей, построенные по образцу класса [`queue.Queue`](https://python-all.ru/3.0/library/queue.html#queue.Queue) из стандартной библиотеки. Они различаются тем, что [`Queue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue) не содержит методов [`task_done()`](https://python-all.ru/3.0/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.0/library/queue.html#queue.Queue.join), добавленных в класс [`queue.Queue`](https://python-all.ru/3.0/library/queue.html#queue.Queue) в Python 2.5.377378Если используется [`JoinableQueue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue), то **необходимо** вызывать [`JoinableQueue.task_done()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди; иначе семафор, используемый для подсчёта незавершённых задач, может в конечном итоге переполниться, вызвав исключение.379380Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [*Менеджеры*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-managers).381382> **Примечание**383>384> `multiprocessing` использует обычные исключения [`queue.Empty`](https://python-all.ru/3.0/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.0/library/queue.html#queue.Full) для сигнализации о тайм-ауте. Они недоступны в пространстве имён `multiprocessing`, поэтому их необходимо импортировать из [`queue`](https://python-all.ru/3.0/library/queue.html#module-queue).385386> **Предупреждение**387>388> Если процесс завершается с помощью [`Process.terminate()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/3.0/library/os.html#os.kill) в то время, как он пытается использовать [`Queue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue), то данные в очереди, скорее всего, окажутся повреждены. Это может привести к тому, что любой другой процесс получит исключение при последующей попытке использовать эту очередь.389390> **Предупреждение**391>392> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не вызывал `JoinableQueue.cancel_join_thread()`), то этот процесс не завершится, пока все буферизованные элементы не будут сброшены в канал.393>394> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.395>396> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [*Рекомендации по программированию*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-programming).397398Пример использования очередей для межпроцессного взаимодействия см. в разделе [*Примеры*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-examples).399400#### `[multiprocessing.Pipe]multiprocessing.Pipe([duplex])`401402Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection), представляющих концы канала.403404Если *duplex* равен `True` (по умолчанию), то канал двунаправленный. Если *duplex* равен `False`, то канал однонаправленный: `conn1` может использоваться только для получения сообщений, а `conn2` – только для отправки сообщений.405406#### `[multiprocessing.Queue]class multiprocessing.Queue([maxsize])`407408Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.409410Обычные исключения [`queue.Empty`](https://python-all.ru/3.0/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.0/library/queue.html#queue.Full) из модуля `Queue` стандартной библиотеки возбуждаются для указания тайм-аутов.411412[`Queue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue) реализует все методы [`queue.Queue`](https://python-all.ru/3.0/library/queue.html#queue.Queue), за исключением [`task_done()`](https://python-all.ru/3.0/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.0/library/queue.html#queue.Queue.join).413414#### `[multiprocessing.Queue.qsize]qsize()`415416Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.417418Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.NotImplementedError) на платформах Unix, таких как Mac OS X, где `sem_getvalue()` не реализован.419420#### `[multiprocessing.Queue.empty]empty()`421422Возвращает423424`True`425426, если очередь пуста, и427428`False`429430в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.431432#### `[multiprocessing.Queue.full]full()`433434Возвращает435436`True`437438, если очередь полна, и439440`False`441442в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.443444#### `[multiprocessing.Queue.put]put(item[, block[, timeout]])`445446Помещает элемент в очередь. Если необязательный аргумент447448*block*449450равен451452`True`453454(по умолчанию), а455456*timeout*457458равен459460`None`461462(по умолчанию), то блокируется при необходимости до появления свободного места. Если463464*timeout*465466– положительное число, то блокируется не более чем на467468*timeout*469470секунд и возбуждает исключение471472[`queue.Full`](https://python-all.ru/3.0/library/queue.html#queue.Full)473474, если свободное место не появилось за это время. В противном случае (475476*block*477478равен479480`False`481482), помещает элемент в очередь, если свободное место доступно немедленно, иначе возбуждает исключение483484[`queue.Full`](https://python-all.ru/3.0/library/queue.html#queue.Full)485486(в этом случае487488*timeout*489490игнорируется).491492#### `[multiprocessing.Queue.put_nowait]put_nowait(item)`493494Эквивалентно495496`put(item, False)`497498.499500#### `[multiprocessing.Queue.get]get([block[, timeout]])`501502Remove and return an item from the queue. If optional args503504*block*505506is507508`True`509510(the default) and511512*timeout*513514is515516`None`517518(the default), block if necessary until an item is available. If519520*timeout*521522is a positive number, it blocks at most523524*timeout*525526seconds and raises the527528[`queue.Empty`](https://python-all.ru/3.0/library/queue.html#queue.Empty)529530exception if no item was available within that time. Otherwise (block is531532`False`533534), return an item if one is immediately available, else raise the535536[`queue.Empty`](https://python-all.ru/3.0/library/queue.html#queue.Empty)537538exception (539540*timeout*541542is ignored in that case).543544#### `[multiprocessing.Queue.get_nowait]get_nowait()`545546#### `get_no_wait()`547548Эквивалентно549550`get(False)`551552.553554У [`multiprocessing.Queue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue) есть несколько дополнительных методов, отсутствующих в [`queue.Queue`](https://python-all.ru/3.0/library/queue.html#queue.Queue). В большинстве кода эти методы обычно не нужны:555556#### `[multiprocessing.Queue.close]close()`557558Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.559560#### `[multiprocessing.Queue.join_thread]join_thread()`561562Присоединяет фоновый поток. Используется только после вызова [`close()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue.close). Блокирует выполнение до завершения фонового потока, гарантируя, что все данные из буфера будут сброшены в канал.563564По умолчанию, если процесс не является создателем очереди, то при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы [`join_thread()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue.join_thread) не делала ничего.565566#### `[multiprocessing.Queue.cancel_join_thread]cancel_join_thread()`567568Предотвращает блокирование569570[`join_thread()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue.join_thread)571572. В частности, это не даёт фоновому потоку автоматически присоединиться при завершении процесса – см.573574[`join_thread()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue.join_thread)575576.577578#### `[multiprocessing.JoinableQueue]class multiprocessing.JoinableQueue([maxsize])`579580[`JoinableQueue`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue), подкласс [`очереди`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно содержит методы [`task_done()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue.join).581582#### `[multiprocessing.JoinableQueue.task_done]task_done()`583584Указывает, что ранее поставленная в очередь задача выполнена. Используется потоками-потребителями очереди. Для каждого вызова [`get()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue.get), используемого для получения задачи, последующий вызов [`task_done()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) сообщает очереди, что обработка задачи завершена.585586Если `join()` в данный момент блокируется, он возобновит работу, когда все элементы будут обработаны (т.е. когда для каждого элемента, помещенного в очередь с помощью [`put()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Queue.put), будет получен вызов [`task_done()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done)).587588Вызывает [`ValueError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.ValueError), если вызывается больше раз, чем было помещено элементов в очередь.589590#### `[multiprocessing.JoinableQueue.join]join()`591592Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.593594Счетчик незавершенных задач увеличивается при каждом добавлении элемента в очередь. Он уменьшается, когда поток-потребитель вызывает [`task_done()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы указать, что элемент получен и вся работа с ним завершена. Когда счетчик незавершенных задач падает до нуля, `join()` разблокируется.595596### Разное597598#### `[multiprocessing.active_children]multiprocessing.active_children()`599600Возвращает список всех активных дочерних процессов текущего процесса.601602Вызов этой функции приводит к «присоединению» любых процессов, которые уже завершились.603604#### `[multiprocessing.cpu_count]multiprocessing.cpu_count()`605606Возвращает количество процессоров в системе. Может вызвать607608[`NotImplementedError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.NotImplementedError)609610.611612#### `[multiprocessing.current_process]multiprocessing.current_process()`613614Возвращает объект [`процесса`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.615616Аналог [`threading.current_thread()`](https://python-all.ru/3.0/library/threading.html#threading.current_thread).617618#### `[multiprocessing.freeze_support]multiprocessing.freeze_support()`619620Добавляет поддержку для случая, когда программа, использующая `multiprocessing`, была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)621622Эту функцию нужно вызывать сразу после строки `if __name__ == '__main__'` главного модуля. Например:623624```python625from multiprocessing import Process, freeze_support626627def f():628    print('hello world!')629630if __name__ == '__main__':631    freeze_support()632    Process(target=f).start()633```634635Если строка `freeze_support()` пропущена, то попытка запустить замороженный исполняемый файл вызовет [`RuntimeError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.RuntimeError).636637Если модуль запускается обычным образом интерпретатором Python, то [`freeze_support()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.freeze_support) не действует.638639#### `[multiprocessing.set_executable]multiprocessing.set_executable()`640641Устанавливает путь к интерпретатору Python, который будет использоваться при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/3.0/library/sys.html#sys.executable)). Встраивающим, вероятно, потребуется сделать что-то вроде642643```python644  setExecutable(os.path.join(sys.exec_prefix, 'pythonw.exe'))645646before they can create child processes.  (Windows only)647```648649> **Примечание**650>651> `multiprocessing` не содержит аналогов [`threading.active_count()`](https://python-all.ru/3.0/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/3.0/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/3.0/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/3.0/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/3.0/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/3.0/library/threading.html#threading.local).652653### Объекты подключения654655Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.656657Объекты соединения обычно создаются с помощью [`Pipe()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Pipe) – см. также [*Listeners and Clients*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-listeners-clients).658659#### `[multiprocessing.Connection]class multiprocessing.Connection`660661#### `[multiprocessing.Connection.send]send(obj)`662663Отправляет объект на другой конец соединения; этот объект должен быть прочитан с помощью [`recv()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection.recv).664665Объект должен быть сериализуемым (picklable).666667#### `[multiprocessing.Connection.recv]recv()`668669Возвращает объект, отправленный с другого конца соединения с помощью670671[`send()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection.send)672673. Возбуждает674675[`EOFError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.EOFError)676677, если больше нечего получать и другой конец был закрыт.678679#### `[multiprocessing.Connection.fileno]fileno()`680681Возвращает файловый дескриптор или дескриптор, используемый соединением.682683#### `[multiprocessing.Connection.close]close()`684685Закрывает подключение.686687Вызывается автоматически при сборке мусора для подключения.688689#### `[multiprocessing.Connection.poll]poll([timeout])`690691Возвращает, есть ли данные для чтения.692693Если *timeout* не указан, то возврат происходит немедленно. Если *timeout* – число, оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, используется бесконечное ожидание.694695#### `[multiprocessing.Connection.send_bytes]send_bytes(buffer[, offset[, size]])`696697Отправляет байтовые данные из объекта, поддерживающего интерфейс буфера, в виде целого сообщения.698699Если задан *offset*, то данные читаются с этой позиции в *buffer*. Если задан *size*, то из буфера будет прочитано соответствующее количество байт.700701#### `[multiprocessing.Connection.recv_bytes]recv_bytes([maxlength])`702703Возвращает полное сообщение с байтовыми данными, отправленное с другого конца соединения, в виде строки. Возбуждает [`EOFError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.EOFError), если больше нечего получать и другой конец был закрыт.704705Если задан *maxlength* и сообщение длиннее *maxlength*, то генерируется [`IOError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.IOError), и соединение становится непригодным для чтения.706707#### `[multiprocessing.Connection.recv_bytes_into]recv_bytes_into(buffer[, offset])`708709Считывает в *buffer* полное сообщение с байтовыми данными, отправленное с другого конца соединения, и возвращает количество байт в сообщении. Возбуждает [`EOFError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.EOFError), если больше нечего получать и другой конец был закрыт.710711*buffer* must be an object satisfying the writable buffer interface. If *offset* is given then the message will be written into the buffer from *that position. Offset must be a non-negative integer less than the \*length of \*buffer* (in bytes).712713Если буфер слишком мал, то вызывается исключение [`BufferTooShort`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.BufferTooShort), и полное сообщение доступно как `e.args[0]`, где `e` – это экземпляр исключения.714715Например:716717```python718>>> from multiprocessing import Pipe719>>> a, b = Pipe()720>>> a.send([1, 'hello', None])721>>> b.recv()722[1, 'hello', None]723>>> b.send_bytes('thank you')724>>> a.recv_bytes()725'thank you'726>>> import array727>>> arr1 = array.array('i', range(5))728>>> arr2 = array.array('i', [0] * 10)729>>> a.send_bytes(arr1)730>>> count = b.recv_bytes_into(arr2)731>>> assert count == len(arr1) * arr1.itemsize732>>> arr2733array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])734```735736> **Предупреждение**737>738> Метод [`Connection.recv()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection.recv) автоматически десериализует полученные данные, что может представлять угрозу безопасности, если нет доверия к процессу, отправившему сообщение.739>740> Поэтому, если объект соединения не был создан с помощью [`Pipe()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Pipe), методы [`recv()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection.recv) и [`send()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection.send) следует использовать только после выполнения некоторой аутентификации. См. [*Authentication keys*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-auth-keys).741742> **Предупреждение**743>744> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.745746### Примитивы синхронизации747748Обычно примитивы синхронизации не так необходимы в многопроцессной программе, как в многопоточной. См. документацию модуля [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading).749750Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [*Менеджеры*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-managers).751752#### `[multiprocessing.BoundedSemaphore]class multiprocessing.BoundedSemaphore([value])`753754Объект ограниченного семафора: копия [`threading.BoundedSemaphore`](https://python-all.ru/3.0/library/threading.html#threading.BoundedSemaphore).755756(На Mac OS X это неотличимо от [`Semaphore`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализована на этой платформе).757758#### `[multiprocessing.Condition]class multiprocessing.Condition([lock])`759760Условная переменная: клон [`threading.Condition`](https://python-all.ru/3.0/library/threading.html#threading.Condition).761762If *lock* is specified then it should be a [`Lock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Lock) or [`RLock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.RLock) object from `multiprocessing`.763764#### `[multiprocessing.Event]class multiprocessing.Event`765766Клон767768[`threading.Event`](https://python-all.ru/3.0/library/threading.html#threading.Event)769770.771772#### `[multiprocessing.Lock]class multiprocessing.Lock`773774Объект нерекурсивной блокировки: копия775776[`threading.Lock`](https://python-all.ru/3.0/library/threading.html#threading.Lock)777778.779780#### `[multiprocessing.RLock]class multiprocessing.RLock`781782Объект рекурсивной блокировки: копия783784[`threading.RLock`](https://python-all.ru/3.0/library/threading.html#threading.RLock)785786.787788#### `[multiprocessing.Semaphore]class multiprocessing.Semaphore([value])`789790Объект ограниченного семафора: клон791792[`threading.Semaphore`](https://python-all.ru/3.0/library/threading.html#threading.Semaphore)793794.795796> **Примечание**797>798> Метод `acquire()` объектов [`BoundedSemaphore`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Lock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.RLock) и [`Semaphore`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Semaphore) имеет параметр timeout, который не поддерживается аналогичными объектами в модуле [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading). Сигнатура: `acquire(block=True, timeout=None)`, при этом допускается передача именованных аргументов. Если *block* равен `True`, а *timeout* не равен `None`, то параметр задает тайм-аут в секундах. Если *block* равен `False`, то *timeout* игнорируется.799>800> Обратите внимание, что на OS/X `sem_timedwait` не поддерживается, поэтому аргументы тайм-аута для них будут проигнорированы.801802> **Примечание**803>804> Если сигнал SIGINT, сгенерированный Ctrl-C, поступает в то время, как главный поток заблокирован вызовом `BoundedSemaphore.acquire()`, `Lock.acquire()`, `RLock.acquire()`, `Semaphore.acquire()`, `Condition.acquire()` или `Condition.wait()`, то вызов будет немедленно прерван и будет возбуждено [`KeyboardInterrupt`](https://python-all.ru/3.0/library/exceptions.html#exceptions.KeyboardInterrupt).805>806> Это отличается от поведения [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading), где SIGINT будет игнорироваться, пока выполняются эквивалентные блокирующие вызовы.807808### Общие [`ctypes`](https://python-all.ru/3.0/library/ctypes.html#module-ctypes) объекты809810Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.811812#### `[multiprocessing.Value]multiprocessing.Value(typecode_or_type, *args[, lock])`813814Возвращает объект [`ctypes`](https://python-all.ru/3.0/library/ctypes.html#module-ctypes), выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для этого объекта.815816*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.0/library/array.html#module-array). *\*args* передаётся конструктору типа.817818Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».819820Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.821822#### `[multiprocessing.Array]multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`823824Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.825826*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.0/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* является последовательностью, которая используется для инициализации массива, и её длина определяет длину массива.827828Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».829830Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.831832Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.0/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.833834#### `multiprocessing.sharedctypes` модуль835836Модуль `multiprocessing.sharedctypes` предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/3.0/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.837838> **Примечание**839>840> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.841842#### `[multiprocessing.sharedctypes.RawArray]multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`843844Возвращает массив ctypes, выделенный из разделяемой памяти.845846*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.0/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно задаёт длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.847848Обратите внимание, что запись и чтение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.849850#### `[multiprocessing.sharedctypes.RawValue]multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`851852Возвращает объект ctypes, выделенный из разделяемой памяти.853854*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.0/library/array.html#module-array). *\*args* передаётся конструктору типа.855856Обратите внимание, что запись и чтение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.857858Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.0/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют хранить и извлекать строки – см. документацию по [`ctypes`](https://python-all.ru/3.0/library/ctypes.html#module-ctypes).859860#### `[multiprocessing.sharedctypes.Array]multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *args[, lock])`861862То же, что и [`RawArray()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного массива ctypes.863864Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом `Lock` или `RLock`, то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».865866Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.867868#### `[multiprocessing.sharedctypes.Value]multiprocessing.sharedctypes.Value(typecode_or_type, *args[, lock])`869870То же, что и [`RawValue()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного объекта ctypes.871872Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом `Lock` или `RLock`, то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».873874Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.875876#### `[multiprocessing.sharedctypes.copy]multiprocessing.sharedctypes.copy(obj)`877878Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes879880*obj*881882.883884#### `[multiprocessing.sharedctypes.synchronized]multiprocessing.sharedctypes.synchronized(obj[, lock])`885886Возвращает процесс-безопасный объект-обёртку для объекта ctypes, который использует *блокировка* для синхронизации доступа. Если *блокировка* равно `None` (по умолчанию), то автоматически создаётся объект [`multiprocessing.RLock`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.RLock).887888Синхронизированная обёртка будет иметь два дополнительных метода по сравнению с объектом, который она оборачивает: `get_obj()` возвращает обёрнутый объект, а `get_lock()` возвращает объект блокировки, используемый для синхронизации.889890Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.891892В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некий подкласс [`ctypes.Structure`](https://python-all.ru/3.0/library/ctypes.html#ctypes.Structure).)893894| ctypes | sharedctypes с типом | sharedctypes с typecode |895| --- | --- | --- |896| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |897| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) |  |898| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |899| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |900901Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:902903```python904from multiprocessing import Process, Lock905from multiprocessing.sharedctypes import Value, Array906from ctypes import Structure, c_double907908class Point(Structure):909    _fields_ = [('x', c_double), ('y', c_double)]910911def modify(n, x, s, A):912    n.value **= 2913    x.value **= 2914    s.value = s.value.upper()915    for a in A:916        a.x **= 2917        a.y **= 2918919if __name__ == '__main__':920    lock = Lock()921922    n = Value('i', 7)923    x = Value(ctypes.c_double, 1.0/3.0, lock=False)924    s = Array('c', 'hello world', lock=lock)925    A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)926927    p = Process(target=modify, args=(n, x, s, A))928    p.start()929    p.join()930931    print(n.value)932    print(x.value)933    print(s.value)934    print([(a.x, a.y) for a in A])935```936937Выводятся следующие результаты:938939```text940499410.1111111111111111942HELLO WORLD943[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]944```945946### Менеджеры947948Менеджеры позволяют создавать данные, которыми могут обмениваться разные процессы. Объект менеджера управляет серверным процессом, который ведает *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.949950#### `[multiprocessing.sharedctypes.multiprocessing.Manager]multiprocessing.Manager()`951952Возвращает запущенный объект953954[`SyncManager`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.SyncManager)955956, который можно использовать для обмена объектами между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.957958Процессы менеджера завершаются сразу после сборки мусора или завершения родительского процесса. Классы менеджеров определены в модуле `multiprocessing.managers`:959960#### `[multiprocessing.managers.BaseManager]class multiprocessing.managers.BaseManager([address[, authkey]])`961962Создает объект BaseManager.963964После создания следует вызвать [`start()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или [`serve_forever()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager.serve_forever), чтобы убедиться, что объект менеджера ссылается на запущенный процесс менеджера.965966*address* – это адрес, по которому процесс менеджера ожидает новые подключения. Если *address* равен `None`, то выбирается произвольный.967968*authkey* – это ключ аутентификации, который используется для проверки подлинности входящих подключений к серверному процессу. Если *authkey* равен `None`, то используется `current_process().authkey`. В противном случае используется *authkey*, который должен быть строкой.969970#### `[multiprocessing.managers.BaseManager.start]start()`971972Запускает подпроцесс для запуска менеджера.973974#### `[multiprocessing.managers.BaseManager.serve_forever]serve_forever()`975976Запускает сервер в текущем процессе.977978#### `[multiprocessing.managers.BaseManager.from_address]from_address(address, authkey)`979980Метод класса, который создаёт объект менеджера, ссылающийся на уже существующий процесс сервера, использующий заданный адрес и ключ аутентификации.981982#### `[multiprocessing.managers.BaseManager.get_server]get_server()`983984Возвращает объект `Server`, который представляет фактический сервер под управлением Manager. Объект `Server` поддерживает метод [`serve_forever()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager.serve_forever):985986```python987>>> from multiprocessing.managers import BaseManager988>>> m = BaseManager(address=('', 50000), authkey='abc'))989>>> server = m.get_server()990>>> s.serve_forever()991```992993`Server` дополнительно имеет атрибут [`address`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).994995#### `[multiprocessing.managers.BaseManager.connect]connect()`996997Подключает локальный объект менеджера к удаленному процессу менеджера:998999```python1000>>> from multiprocessing.managers import BaseManager1001>>> m = BaseManager(address='127.0.0.1', authkey='abc))>>> 1002m.connect()1003```10041005#### `[multiprocessing.managers.BaseManager.shutdown]shutdown()`10061007Останавливает процесс, используемый менеджером. Это доступно только в том случае, если [`start()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.10081009Этот метод можно вызывать несколько раз.10101011#### `[multiprocessing.managers.BaseManager.register]register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`10121013Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.10141015*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.10161017*callable* – это вызываемый объект, используемый для создания объектов для данного идентификатора типа. Если экземпляр менеджера будет создаваться через метод класса [`from_address()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager.from_address) или если аргумент *create\_method* равен `False`, то это можно оставить как `None`.10181019*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.10201021*exposed* используется для указания последовательности имён методов, к которым прокси для этого typeid должны иметь доступ через `BaseProxy._callMethod()`. (Если *exposed* равен `None`, то используется `proxytype._exposed_`, если он существует.) В случае, когда список exposed не указан, все «открытые методы» разделяемого объекта будут доступны. (Здесь «открытый метод» означает любой атрибут, который имеет метод [`__call__()`](https://python-all.ru/3.0/reference/datamodel.html#object.__call__) и чьё имя не начинается с `'_'`.)10221023*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно сопоставляет имена методов со строками typeid. (Если *method\_to\_typeid* равен `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом в этом отображении или если отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.10241025*create\_method* определяет, должен ли быть создан метод с именем *typeid*, который можно использовать, чтобы сообщить серверному процессу о создании нового разделяемого объекта и возврате прокси для него. По умолчанию это `True`.10261027Экземпляры [`BaseManager`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:10281029#### `[multiprocessing.managers.BaseManager.address]address`10301031Адрес, используемый менеджером.10321033#### `[multiprocessing.managers.SyncManager]class multiprocessing.managers.SyncManager`10341035Подкласс [`BaseManager`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.10361037Он также поддерживает создание разделяемых списков и словарей.10381039#### `[multiprocessing.managers.SyncManager.BoundedSemaphore]BoundedSemaphore([value])`10401041Создаёт разделяемый объект10421043[`threading.BoundedSemaphore`](https://python-all.ru/3.0/library/threading.html#threading.BoundedSemaphore)10441045и возвращает прокси для него.10461047#### `[multiprocessing.managers.SyncManager.Condition]Condition([lock])`10481049Создаёт разделяемый объект [`threading.Condition`](https://python-all.ru/3.0/library/threading.html#threading.Condition) и возвращает прокси для него.10501051Если *блокировка* указан, то он должен быть прокси для объекта [`threading.Lock`](https://python-all.ru/3.0/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/3.0/library/threading.html#threading.RLock).10521053#### `[multiprocessing.managers.SyncManager.Event]Event()`10541055Создаёт разделяемый объект10561057[`threading.Event`](https://python-all.ru/3.0/library/threading.html#threading.Event)10581059и возвращает для него прокси.10601061#### `[multiprocessing.managers.SyncManager.Lock]Lock()`10621063Создаёт разделяемый объект10641065[`threading.Lock`](https://python-all.ru/3.0/library/threading.html#threading.Lock)10661067и возвращает для него прокси.10681069#### `[multiprocessing.managers.SyncManager.Namespace]Namespace()`10701071Создаёт разделяемый объект10721073[`Namespace`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.SyncManager.Namespace)10741075и возвращает для него прокси.10761077#### `[multiprocessing.managers.SyncManager.Queue]Queue([maxsize])`10781079Создаёт разделяемый объект10801081[`queue.Queue`](https://python-all.ru/3.0/library/queue.html#queue.Queue)10821083и возвращает для него прокси.10841085#### `[multiprocessing.managers.SyncManager.RLock]RLock()`10861087Создаёт разделяемый объект10881089[`threading.RLock`](https://python-all.ru/3.0/library/threading.html#threading.RLock)10901091и возвращает для него прокси.10921093#### `[multiprocessing.managers.SyncManager.Semaphore]Semaphore([value])`10941095Создаёт разделяемый объект10961097[`threading.Semaphore`](https://python-all.ru/3.0/library/threading.html#threading.Semaphore)10981099и возвращает прокси для него.11001101#### `[multiprocessing.managers.SyncManager.Array]Array(typecode, sequence)`11021103Создать массив и вернуть для него прокси.11041105#### `[multiprocessing.managers.SyncManager.Value]Value(typecode, value)`11061107Создаёт объект с атрибутом11081109`value`11101111, доступным для записи, и возвращает прокси для него.11121113#### `[multiprocessing.managers.SyncManager.dict]dict()`11141115#### `dict(mapping)`11161117#### `dict(sequence)`11181119Создаёт разделяемый объект11201121`dict`11221123и возвращает для него прокси.11241125#### `[multiprocessing.managers.SyncManager.list]list()`11261127#### `list(sequence)`11281129Создаёт разделяемый объект11301131`list`11321133и возвращает для него прокси.11341135#### объекты Namespace11361137Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.11381139Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:11401141```python1142>>> manager = multiprocessing.Manager()1143>>> Global = manager.Namespace()1144>>> Global.x = 101145>>> Global.y = 'hello'1146>>> Global._z = 12.3    # это атрибут прокси1147>>> print(Global)1148Namespace(x=10, y='hello')1149```11501151#### Настраиваемые менеджеры11521153Чтобы создать собственный менеджер, нужно создать подкласс [`BaseManager`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager) и использовать метод класса [`register()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:11541155```python1156from multiprocessing.managers import BaseManager11571158class MathsClass(object):1159    def add(self, x, y):1160        return x + y1161    def mul(self, x, y):1162        return x * y11631164class MyManager(BaseManager):1165    pass11661167MyManager.register('Maths', MathsClass)11681169if __name__ == '__main__':1170    manager = MyManager()1171    manager.start()1172    maths = manager.Maths()1173    print(maths.add(4, 3))         # выводит 71174    print(maths.mul(7, 8))         # выводит 561175```11761177#### Использование удалённого менеджера11781179Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).11801181Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:11821183```python1184>>> from multiprocessing.managers import BaseManager1185>>> import queue1186>>> queue = queue.Queue()1187>>> class QueueManager(BaseManager): pass1188...1189>>> QueueManager.register('get_queue', callable=lambda:queue)1190>>> m = QueueManager(address=('', 50000), authkey='abracadabra')1191>>> s = m.get_server()1192>>> s.serveForever()1193```11941195Один клиент может получить доступ к серверу следующим образом:11961197```python1198>>> from multiprocessing.managers import BaseManager1199>>> class QueueManager(BaseManager): pass1200...1201>>> QueueManager.register('get_queue')1202>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')1203>>> m.connect()1204>>> queue = m.get_queue()1205>>> queue.put('hello')1206```12071208Другой клиент также может использовать его:12091210```python1211>>> from multiprocessing.managers import BaseManager1212>>> class QueueManager(BaseManager): pass1213...1214>>> QueueManager.register('getQueue')1215>>> m = QueueManager.from_address(address=('foo.bar.org', 50000), authkey='abracadabra')1216>>> queue = m.getQueue()1217>>> queue.get()1218'hello'1219```12201221Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:12221223```python1224>>> from multiprocessing import Process, Queue1225>>> from multiprocessing.managers import BaseManager1226>>> class Worker(Process):1227...     def __init__(self, q):1228...         self.q = q1229...         super(Worker, self).__init__()1230...     def run(self):1231...         self.q.put('local hello')1232...1233>>> queue = Queue()1234>>> w = Worker(queue)1235>>> w.start()1236>>> class QueueManager(BaseManager): pass1237...1238>>> QueueManager.register('get_queue', callable=lambda: queue)1239>>> m = QueueManager(address=('', 50000), authkey='abracadabra')1240>>> s = m.get_server()1241>>> s.serve_forever()1242```12431244### Объекты-прокси12451246Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.12471248Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:12491250```python1251>>> from multiprocessing import Manager1252>>> manager = Manager()1253>>> l = manager.list([i*i for i in range(10)])1254>>> print(l)1255[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1256>>> print(repr(l))1257<ListProxy object, typeid 'list' at 0xb799974c>1258>>> l[4]1259161260>>> l[2:5]1261[4, 9, 16]1262```12631264Обратите внимание, что применение [`str()`](https://python-all.ru/3.0/library/functions.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/3.0/library/functions.html#repr) вернёт представление прокси.12651266Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:12671268```python1269>>> a = manager.list()1270>>> b = manager.list()1271>>> a.append(b)         # референт a теперь содержит референт b1272>>> print(a, b)1273[[]] []1274>>> b.append('hello')1275>>> print(a, b)1276[['hello']] ['hello']1277```12781279> **Примечание**1280>1281> Типы прокси в `multiprocessing` не поддерживают сравнение по значению. Так, например,1282>1283> ```python1284> manager.list([1,2,3]) == [1,2,3]1285> ```1286>1287> вернёт `False`. Вместо этого для сравнений следует использовать копию объекта, на который ссылается прокси.12881289#### `[multiprocessing.managers.BaseProxy]class multiprocessing.managers.BaseProxy`12901291Объекты прокси являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseProxy).12921293#### `[multiprocessing.managers.BaseProxy._callmethod]_callmethod(methodname[, args[, kwds]])`12941295Вызывает метод референта прокси и возвращает результат.12961297Если `proxy` является прокси, чей референт – `obj`, то выражение12981299```python1300proxy._callmethod(methodname, args, kwds)1301```13021303вычислит выражение13041305```python1306getattr(obj, methodname)(*args, **kwds)1307```13081309в процессе менеджера.13101311Возвращаемое значение будет копией результата вызова или прокси на новый разделяемый объект – см. документацию по аргументу *method\_to\_typeid* метода [`BaseManager.register()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).13121313Если вызов вызывает исключение, то оно повторно возбуждается методом [`_callmethod()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). Если в процессе менеджера возникает другое исключение, оно преобразуется в исключение `RemoteError` и возбуждается методом [`_callmethod()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod).13141315Обратите внимание: если *methodname* не был *опубликован*, будет возбуждено исключение.13161317Пример использования [`_callmethod()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):13181319```python1320>>> l = manager.list(range(10))1321>>> l._callmethod('__len__')1322101323>>> l._callmethod('__getslice__', (2, 7))   # эквивалентно `l[2:7]`1324[2, 3, 4, 5, 6]1325>>> l._callmethod('__getitem__', (20,))     # эквивалентно `l[20]`1326...1327IndexError: list index out of range1328```13291330#### `[multiprocessing.managers.BaseProxy._getvalue]_getvalue()`13311332Возвращает копию референта.13331334Если референт не может быть сериализован (unpicklable), то это вызовет исключение.13351336#### `[multiprocessing.managers.BaseProxy.__repr__]__repr__()`13371338Возвращает представление прокси-объекта.13391340#### `[multiprocessing.managers.BaseProxy.__str__]__str__()`13411342Возвращает представление референта.13431344#### Очистка13451346Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.13471348Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.13491350### Пулы процессов13511352Можно создать пул процессов, которые будут выполнять отправленные им задачи с помощью класса `Pool`.13531354#### `[multiprocessing.pool.multiprocessing.Pool]class multiprocessing.Pool([processes[, initializer[, initargs]]])`13551356Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.13571358*processes* – количество используемых рабочих процессов. Если *processes* равно `None`, используется число, возвращаемое `cpu_count()`. Если *initializer* не `None`, каждый рабочий процесс при запуске вызовет `initializer(*initargs)`.13591360#### `[multiprocessing.pool.multiprocessing.Pool.apply]apply(func[, args[, kwds]])`13611362Вызывает13631364*func*13651366с аргументами13671368*args*13691370и именованными аргументами13711372*kwds*13731374. Блокируется до тех пор, пока результат не будет готов.13751376#### `[multiprocessing.pool.multiprocessing.Pool.apply_async]apply_async(func[, args[, kwds[, callback]]])`13771378Вариант метода [`apply()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.apply), который возвращает объект результата.13791380Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.13811382#### `[multiprocessing.pool.multiprocessing.Pool.map]map(func, iterable[, chunksize])`13831384Параллельный аналог встроенной функции [`map()`](https://python-all.ru/3.0/library/functions.html#map), собирающий результат в список. Блокируется до тех пор, пока не будет готов весь результат.13851386Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.13871388#### `[multiprocessing.pool.multiprocessing.Pool.map_async]map_async(func, iterable[, chunksize[, callback]])`13891390Вариант метода [`map()`](https://python-all.ru/3.0/library/functions.html#map), который возвращает объект результата.13911392Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.13931394#### `[multiprocessing.pool.multiprocessing.Pool.imap]imap(func, iterable[, chunksize])`13951396Более ленивая версия [`map()`](https://python-all.ru/3.0/library/functions.html#map).13971398Аргумент *chunksize* аналогичен тому, что используется в методе [`map()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map). Для очень длинных итерируемых объектов использование большого значения *chunksize* может сделать выполнение задачи **значительно** быстрее, чем использование значения по умолчанию `1`.13991400Кроме того, если *chunksize* равен `1`, то метод [`next()`](https://python-all.ru/3.0/library/functions.html#next) итератора, возвращаемого методом [`imap()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` вызовет `multiprocessing.TimeoutError`, если результат не может быть возвращён в течение *timeout* секунд.14011402#### `[multiprocessing.pool.multiprocessing.Pool.imap_unordered]imap_unordered(func, iterable[, chunksize])`14031404То же, что и14051406[`imap()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap)14071408, но порядок результатов из возвращаемого итератора может быть произвольным. (Только при наличии одного рабочего процесса порядок гарантированно «правильный».)14091410#### `[multiprocessing.pool.multiprocessing.Pool.close]close()`14111412Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.14131414#### `[multiprocessing.pool.multiprocessing.Pool.terminate]terminate()`14151416Немедленно останавливает рабочие процессы, не завершая незавершённую работу. Когда объект пула собирается сборщиком мусора,14171418[`terminate()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate)14191420будет вызван немедленно.14211422#### `[multiprocessing.pool.multiprocessing.Pool.join]join()`14231424Ожидает завершения рабочих процессов. Перед вызовом14251426[`join()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.join)14271428необходимо вызвать14291430[`close()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.close)14311432или14331434[`terminate()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate)14351436.14371438#### `[multiprocessing.pool.AsyncResult]class multiprocessing.pool.AsyncResult`14391440Класс результата, возвращаемого методами `Pool.apply_async()` и `Pool.map_async()`.14411442#### `[multiprocessing.pool.AsyncResult.get]get([timeout])`14431444Возвращает результат, когда он будет получен. Если14451446*timeout*14471448не равно14491450`None`14511452и результат не получен в течение14531454*timeout*14551456секунд, то возбуждается14571458`multiprocessing.TimeoutError`14591460. Если удалённый вызов возбудил исключение, то это исключение будет повторно возбуждено методом14611462[`get()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get)14631464.14651466#### `[multiprocessing.pool.AsyncResult.wait]wait([timeout])`14671468Ожидает, пока результат не станет доступен или пока не пройдёт14691470*timeout*14711472секунд.14731474#### `[multiprocessing.pool.AsyncResult.ready]ready()`14751476Возвращает, завершён ли вызов.14771478#### `[multiprocessing.pool.AsyncResult.successful]successful()`14791480Возвращает, завершился ли вызов без возбуждения исключения. Возбуждает14811482[`AssertionError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.AssertionError)14831484, если результат ещё не готов.14851486Следующий пример демонстрирует использование пула:14871488```python1489from multiprocessing import Pool14901491def f(x):1492    return x*x14931494if __name__ == '__main__':1495    pool = Pool(processes=4)              # запустить 4 рабочих процесса14961497    result = pool.apply_async(f, (10,))   # асинхронно вычислить "f(10)"1498    print(result.get(timeout=1))          # выводит "100", если компьютер не *очень* медленный14991500    print(pool.map(f, range(10)))         # выводит "[0, 1, 4,..., 81]"15011502    it = pool.imap(f, range(10))1503    print(next(it))                       # выводит "0"1504    print(next(it))                       # выводит "1"1505    print(it.next(timeout=1))             # выводит "4", если компьютер не *очень* медленный15061507    import time1508    result = pool.apply_async(time.sleep, (10,))1509    print(result.get(timeout=1))          # вызывает TimeoutError1510```15111512### Слушатели и клиенты15131514Обычно обмен сообщениями между процессами осуществляется с помощью очередей или с использованием объектов `Connection`, возвращаемых `Pipe()`.15151516Однако модуль `multiprocessing.connection` предоставляет дополнительную гибкость. По сути, он предлагает высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows, а также поддерживает *аутентификацию с помощью дайджеста* с использованием модуля [`hmac`](https://python-all.ru/3.0/library/hmac.html#module-hmac).15171518#### `[multiprocessing.connection.deliver_challenge]multiprocessing.connection.deliver_challenge(connection, authkey)`15191520Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.15211522Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).15231524#### `[multiprocessing.connection.answerChallenge]multiprocessing.connection.answerChallenge(connection, authkey)`15251526Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.15271528Если приветственное сообщение не получено, то возникает исключение [`AuthenticationError`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).15291530#### `[multiprocessing.connection.Client]multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`15311532Пытается установить соединение с прослушивателем, который использует адрес *address*, и возвращает объект [`Connection`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Connection).15331534Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [*Address Formats*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-address-formats))15351536Если *authentication* равно `True` или *authkey* является строкой, используется digest-аутентификация. Ключ аутентификации будет либо *authkey*, либо `current_process().authkey)`, если *authkey* равно `None`. Если аутентификация не удалась, возбуждается [`AuthenticationError`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.connection.AuthenticationError). См. [*Ключи аутентификации*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-auth-keys).15371538#### `[multiprocessing.connection.Listener]class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`15391540Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.15411542*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.15431544*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них только первый гарантированно доступен. Если *family* равен `None`, то семейство определяется по формату *address*. Если *address* также равен `None`, то выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [*Address Formats*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равен `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/3.0/library/tempfile.html#tempfile.mkstemp).15451546Если объект прослушивателя использует сокет, то *backlog* (по умолчанию 1) передаётся методу `listen()` сокета после его привязки.15471548Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.15491550Если *authkey* – строка, то она будет использоваться как ключ аутентификации; в противном случае должно быть *None*.15511552Если *authkey* равно `None`, и *authenticate* равно `True`, то `current_process().authkey` используется в качестве ключа аутентификации. Если *authkey* равно `None` и *authentication* равно `False`, то аутентификация не выполняется. Если аутентификация не удалась, возбуждается [`AuthenticationError`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.connection.AuthenticationError). См. [*Ключи аутентификации*](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing-auth-keys).15531554#### `[multiprocessing.connection.Listener.accept]accept()`15551556Принимает соединение на привязанном сокете или именованном канале объекта прослушивателя и возвращает объект15571558`Connection`15591560. Если аутентификация запрошена и не удалась, то возникает исключение15611562[`AuthenticationError`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.connection.AuthenticationError)15631564.15651566#### `[multiprocessing.connection.Listener.close]close()`15671568Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.15691570Объекты Listener имеют следующие свойства только для чтения:15711572#### `[multiprocessing.connection.Listener.address]address`15731574Адрес, используемый объектом Listener.15751576#### `[multiprocessing.connection.Listener.last_accepted]last_accepted`15771578Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то15791580`None`15811582.15831584Модуль определяет два исключения:15851586#### `[multiprocessing.connection.AuthenticationError]exception multiprocessing.connection.AuthenticationError`15871588Исключение, возникающее при ошибке аутентификации.15891590**Примеры**15911592Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:15931594```python1595from multiprocessing.connection import Listener1596from array import array15971598address = ('localhost', 6000)     # семейство определяется как 'AF_INET'1599listener = Listener(address, authkey='secret password')16001601conn = listener.accept()1602print('connection accepted from', listener.last_accepted)16031604conn.send([2.25, None, 'junk', float])16051606conn.send_bytes('hello')16071608conn.send_bytes(array('i', [42, 1729]))16091610conn.close()1611listener.close()1612```16131614Следующий код подключается к серверу и получает от него некоторые данные:16151616```python1617from multiprocessing.connection import Client1618from array import array16191620address = ('localhost', 6000)1621conn = Client(address, authkey='secret password')16221623print(conn.recv())                  # => [2.25, None, 'junk', float]16241625print(conn.recv_bytes())            # => 'hello'16261627arr = array('i', [0, 0, 0, 0, 0])1628print(conn.recv_bytes_into(arr))    # => 81629print(arr)                          # => array('i', [42, 1729, 0, 0, 0])16301631conn.close()1632```16331634#### Форматы адресов16351636- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.1637- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.1638- **Адрес `'AF_PIPE'` – это строка вида**16391640  `r'\\.\pipe\PipeName'`. Чтобы использовать [`Client()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.connection.Client) для подключения к именованному каналу на удалённом компьютере с именем *ServerName*, следует использовать адрес вида `r'\\ServerName\pipe\PipeName'` вместо.16411642Обратите внимание, что любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не адресом `'AF_UNIX'`.16431644### Ключи аутентификации16451646При использовании `Connection.recv()` полученные данные автоматически распаковываются. К сожалению, распаковка данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/3.0/library/hmac.html#module-hmac) для аутентификации по дайджесту.16471648Ключ аутентификации – это строка, которую можно рассматривать как пароль: как только соединение установлено, обе стороны требуют подтверждения, что другая сторона знает ключ аутентификации. (Доказательство того, что обе стороны используют один и тот же ключ, **не** подразумевает отправку ключа по соединению.)16491650Если аутентификация запрошена, но ключ аутентификации не указан, используется возвращаемое значение `current_process().authkey` (см. [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process)). Это значение автоматически наследуется любым объектом [`Process`](https://python-all.ru/3.0/library/multiprocessing.html#multiprocessing.Process), создаваемым текущим процессом. Это означает, что (по умолчанию) все процессы многопроцессной программы будут использовать единый ключ аутентификации, который можно использовать при настройке соединений между ними.16511652Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/3.0/library/os.html#os.urandom).16531654### Журналирование16551656Доступна некоторая поддержка логирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/3.0/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому возможно (в зависимости от типа обработчика) перемешивание сообщений от разных процессов.16571658#### `[multiprocessing.get_logger]multiprocessing.get_logger()`16591660Возвращает регистратор (logger), используемый модулем `multiprocessing`. При необходимости будет создан новый.16611662При первом создании логгер имеет уровень `logging.NOTSET` и обработчик, который отправляет вывод в [`sys.stderr`](https://python-all.ru/3.0/library/sys.html#sys.stderr), используя формат `'[%(levelname)s/%(processName)s] %(message)s'`. (Логгер допускает использование нестандартного формата `'%(processName)s'`.) Сообщения, отправленные этому логгеру, по умолчанию не распространяются на корневой логгер.16631664Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.16651666Ниже приведён пример сеанса с включённым журналированием:16671668```python1669>>> import multiprocessing, logging1670>>> logger = multiprocessing.get_logger()1671>>> logger.setLevel(logging.INFO)1672>>> logger.warning('doomed')1673[WARNING/MainProcess] doomed1674>>> m = multiprocessing.Manager()1675[INFO/SyncManager-1] child process calling self.run()1676[INFO/SyncManager-1] manager bound to '\\\\.\\pipe\\pyc-2776-0-lj0tfa'1677>>> del m1678[INFO/MainProcess] sending shutdown message to manager1679[INFO/SyncManager-1] manager exiting with exitcode 01680```16811682### Модуль `multiprocessing.dummy`16831684`multiprocessing.dummy` повторяет API модуля `multiprocessing`, но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading).16851686## Рекомендации по программированию16871688При использовании `multiprocessing` следует придерживаться определённых правил и идиом.16891690### Все платформы16911692Избегайте разделяемого состояния16931694> По возможности следует стараться избегать передачи больших объёмов данных между процессами.1695>1696> Вероятно, лучше использовать очереди или каналы для связи между процессами, чем низкоуровневые примитивы синхронизации из модуля [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading).16971698Сериализуемость16991700> Убедитесь, что аргументы методов прокси-объектов сериализуемы.17011702Потокобезопасность прокси17031704> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.1705>1706> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)17071708Присоединение процессов-зомби17091710> В Unix, когда процесс завершается, но к нему не был присоединён вызовом join, он становится зомби. Их не должно быть много, потому что каждый раз при запуске нового процесса (или вызове1711>1712> `active_children()`1713>1714> ) все завершённые процессы, к которым ещё не был применён join, будут присоединены. Также вызов1715>1716> `Process.is_alive()`1717>1718> для завершённого процесса присоединит его. Тем не менее, считается хорошей практикой явно присоединять все запущенные процессы.17191720Лучше наследовать, чем упаковывать/распаковывать17211722> В Windows многие типы из1723>1724> `multiprocessing`1725>1726> должны быть сериализуемыми (picklable), чтобы дочерние процессы могли их использовать. Однако, как правило, следует избегать отправки разделяемых объектов другим процессам через каналы (pipes) или очереди (queues). Вместо этого программу следует организовать так, чтобы процесс, которому нужен доступ к разделяемому ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.17271728Избегайте завершения процессов17291730> Использование метода `Process.terminate()` для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), используемые в данный момент процессом, станут повреждёнными или недоступными для других процессов.1731>1732> Поэтому, вероятно, лучше рассматривать использование `Process.terminate()` только для процессов, которые никогда не используют разделяемые ресурсы.17331734Присоединение процессов, использующих очереди17351736> Имейте в виду, что процесс, поместивший элементы в очередь, будет ждать перед завершением, пока все буферизованные элементы не будут переданы потоком «feeder» в нижележащий канал. (Дочерний процесс может вызвать метод очереди `Queue.cancel_join_thread()`, чтобы избежать такого поведения.)1737>1738> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещенные в очередь, в конечном итоге будут удалены до того, как процесс будет присоединен (joined). В противном случае нельзя быть уверенным, что процессы, которые помещали элементы в очередь, завершатся. Также помните, что недемонические процессы будут присоединены автоматически.1739>1740> Пример, который приведёт к взаимоблокировке:1741>1742> ```python1743> from multiprocessing import Process, Queue1744>1745> def f(q):1746>     q.put('X' * 1000000)1747>1748> if __name__ == '__main__':1749>     queue = Queue()1750>     p = Process(target=f, args=(queue,))1751>     p.start()1752>     p.join()                    # это приводит к взаимоблокировке1753>     obj = queue.get()1754> ```1755>1756> Исправлением здесь была бы перестановка двух последних строк (или просто удаление строки с `p.join()`).17571758Явная передача ресурсов дочерним процессам17591760> В Unix дочерний процесс может использовать общий ресурс, созданный в родительском процессе, с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.1761>1762> Помимо обеспечения (потенциальной) совместимости с Windows, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если какой-либо ресурс освобождается при сборке мусора в родительском процессе.1763>1764> Так, например1765>1766> ```python1767> from multiprocessing import Process, Lock1768>1769> def f():1770>     ... do something using "lock" ...1771>1772> if __name__ == '__main__':1773>    lock = Lock()1774>    for i in range(10):1775>         Process(target=f).start()1776> ```1777>1778> следует переписать как1779>1780> ```python1781> from multiprocessing import Process, Lock1782>1783> def f(l):1784>     ... do something using "l" ...1785>1786> if __name__ == '__main__':1787>    lock = Lock()1788>    for i in range(10):1789>         Process(target=f, args=(lock,)).start()1790> ```17911792### Windows17931794Поскольку в Windows нет [`os.fork()`](https://python-all.ru/3.0/library/os.html#os.fork), существуют несколько дополнительных ограничений:17951796Упаковываемость17971798> Убедитесь, что все аргументы `Process.__init__()` являются сериализуемыми (picklable). Это означает, в частности, что связанные или несвязанные методы нельзя использовать напрямую в качестве аргумента `target` в Windows – просто определите функцию и используйте ее вместо этого.1799>1800> Кроме того, если вы создаёте подкласс `Process`, убедитесь, что экземпляры будут сериализуемы с помощью pickle при вызове метода `Process.start()`.18011802Глобальные переменные18031804> Имейте в виду, что если код, выполняемый в дочернем процессе, пытается получить доступ к глобальной переменной, то значение, которое он видит (если оно есть), может не совпадать со значением в родительском процессе на момент вызова `Process.start()`.1805>1806> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.18071808Безопасный импорт главного модуля18091810> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).1811>1812> Например, в Windows запуск следующего модуля завершится ошибкой [`RuntimeError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.RuntimeError):1813>1814> ```python1815> from multiprocessing import Process1816>1817> def foo():1818>     print('hello')1819>1820> p = Process(target=foo)1821> p.start()1822> ```1823>1824> Вместо этого следует защитить «точку входа» программы, используя конструкцию `if __name__ == '__main__':` следующим образом:1825>1826> ```python1827> from multiprocessing import Process, freeze_support1828>1829> def foo():1830>     print('hello')1831>1832> if __name__ == '__main__':1833>     freeze_support()1834>     p = Process(target=foo)1835>     p.start()1836> ```1837>1838> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не в замороженном виде.)1839>1840> Это позволяет новому порождённому интерпретатору Python безопасно импортировать модуль, а затем выполнить функцию `foo()` этого модуля.1841>1842> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.18431844## Примеры18451846Демонстрация создания и использования настраиваемых менеджеров и прокси:18471848```python1849#1850# This module shows how to use arbitrary callables with a subclass of1851# `BaseManager`.1852#1853# Copyright (c) 2006-2008, R Oudkerk1854# All rights reserved.1855#18561857from multiprocessing import freeze_support1858from multiprocessing.managers import BaseManager, BaseProxy1859import operator18601861##18621863class Foo(object):1864    def f(self):1865        print('you called Foo.f()')1866    def g(self):1867        print('you called Foo.g()')1868    def _h(self):1869        print('you called Foo._h()')18701871# A simple generator function1872def baz():1873    for i in range(10):1874        yield i*i18751876# Proxy type for generator objects1877class GeneratorProxy(BaseProxy):1878    _exposed_ = ('next', '__next__')1879    def __iter__(self):1880        return self1881    def __next__(self):1882        return self._callmethod('next')1883    def __next__(self):1884        return self._callmethod('__next__')18851886# Function to return the operator module1887def get_operator_module():1888    return operator18891890##18911892class MyManager(BaseManager):1893    pass18941895# register the Foo class; make `f()` and `g()` accessible via proxy1896MyManager.register('Foo1', Foo)18971898# register the Foo class; make `g()` and `_h()` accessible via proxy1899MyManager.register('Foo2', Foo, exposed=('g', '_h'))19001901# register the generator function baz; use `GeneratorProxy` to make proxies1902MyManager.register('baz', baz, proxytype=GeneratorProxy)19031904# register get_operator_module(); make public functions accessible via proxy1905MyManager.register('operator', get_operator_module)19061907##19081909def test():1910    manager = MyManager()1911    manager.start()19121913    print('-' * 20)19141915    f1 = manager.Foo1()1916    f1.f()1917    f1.g()1918    assert not hasattr(f1, '_h')1919    assert sorted(f1._exposed_) == sorted(['f', 'g'])19201921    print('-' * 20)19221923    f2 = manager.Foo2()1924    f2.g()1925    f2._h()1926    assert not hasattr(f2, 'f')1927    assert sorted(f2._exposed_) == sorted(['g', '_h'])19281929    print('-' * 20)19301931    it = manager.baz()1932    for i in it:1933        print('<%d>' % i, end=' ')1934    print()19351936    print('-' * 20)19371938    op = manager.operator()1939    print('op.add(23, 45) =', op.add(23, 45))1940    print('op.pow(2, 94) =', op.pow(2, 94))1941    print('op.getslice(range(10), 2, 6) =', op.getslice(list(range(10)), 2, 6))1942    print('op.repeat(range(5), 3) =', op.repeat(list(range(5)), 3))1943    print('op._exposed_ =', op._exposed_)19441945##19461947if __name__ == '__main__':1948    freeze_support()1949    test()1950```19511952Использование `Pool`:19531954```python1955#1956# A test of `multiprocessing.Pool` class1957#1958# Copyright (c) 2006-2008, R Oudkerk1959# All rights reserved.1960#19611962import multiprocessing1963import time1964import random1965import sys19661967#1968# Functions used by test code1969#19701971def calculate(func, args):1972    result = func(*args)1973    return '%s says that %s%s = %s' % (1974        multiprocessing.current_process().name,1975        func.__name__, args, result1976        )19771978def calculatestar(args):1979    return calculate(*args)19801981def mul(a, b):1982    time.sleep(0.5*random.random())1983    return a * b19841985def plus(a, b):1986    time.sleep(0.5*random.random())1987    return a + b19881989def f(x):1990    return 1.0 / (x-5.0)19911992def pow3(x):1993    return x**319941995def noop(x):1996    pass19971998#1999# Test code2000#20012002def test():2003    print('cpu_count() = %d\n' % multiprocessing.cpu_count())20042005    #2006    # Create pool2007    #20082009    PROCESSES = 42010    print('Creating pool with %d processes\n' % PROCESSES)2011    pool = multiprocessing.Pool(PROCESSES)2012    print('pool = %s' % pool)2013    print()20142015    #2016    # Tests2017    #20182019    TASKS = [(mul, (i, 7)) for i in range(10)] + \2020            [(plus, (i, 8)) for i in range(10)]20212022    results = [pool.apply_async(calculate, t) for t in TASKS]2023    imap_it = pool.imap(calculatestar, TASKS)2024    imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)20252026    print('Ordered results using pool.apply_async():')2027    for r in results:2028        print('\t', r.get())2029    print()20302031    print('Ordered results using pool.imap():')2032    for x in imap_it:2033        print('\t', x)2034    print()20352036    print('Unordered results using pool.imap_unordered():')2037    for x in imap_unordered_it:2038        print('\t', x)2039    print()20402041    print('Ordered results using pool.map() --- will block till complete:')2042    for x in pool.map(calculatestar, TASKS):2043        print('\t', x)2044    print()20452046    #2047    # Simple benchmarks2048    #20492050    N = 1000002051    print('def pow3(x): return x**3')20522053    t = time.time()2054    A = list(map(pow3, range(N)))2055    print('\tmap(pow3, xrange(%d)):\n\t\t%s seconds' % \2056          (N, time.time() - t))20572058    t = time.time()2059    B = pool.map(pow3, range(N))2060    print('\tpool.map(pow3, xrange(%d)):\n\t\t%s seconds' % \2061          (N, time.time() - t))20622063    t = time.time()2064    C = list(pool.imap(pow3, range(N), chunksize=N//8))2065    print('\tlist(pool.imap(pow3, xrange(%d), chunksize=%d)):\n\t\t%s' \2066          ' seconds' % (N, N//8, time.time() - t))20672068    assert A == B == C, (len(A), len(B), len(C))2069    print()20702071    L = [None] * 10000002072    print('def noop(x): pass')2073    print('L = [None] * 1000000')20742075    t = time.time()2076    A = list(map(noop, L))2077    print('\tmap(noop, L):\n\t\t%s seconds' % \2078          (time.time() - t))20792080    t = time.time()2081    B = pool.map(noop, L)2082    print('\tpool.map(noop, L):\n\t\t%s seconds' % \2083          (time.time() - t))20842085    t = time.time()2086    C = list(pool.imap(noop, L, chunksize=len(L)//8))2087    print('\tlist(pool.imap(noop, L, chunksize=%d)):\n\t\t%s seconds' % \2088          (len(L)//8, time.time() - t))20892090    assert A == B == C, (len(A), len(B), len(C))2091    print()20922093    del A, B, C, L20942095    #2096    # Test error handling2097    #20982099    print('Testing error handling:')21002101    try:2102        print(pool.apply(f, (5,)))2103    except ZeroDivisionError:2104        print('\tGot ZeroDivisionError as expected from pool.apply()')2105    else:2106        raise AssertionError('expected ZeroDivisionError')21072108    try:2109        print(pool.map(f, list(range(10))))2110    except ZeroDivisionError:2111        print('\tGot ZeroDivisionError as expected from pool.map()')2112    else:2113        raise AssertionError('expected ZeroDivisionError')21142115    try:2116        print(list(pool.imap(f, list(range(10)))))2117    except ZeroDivisionError:2118        print('\tGot ZeroDivisionError as expected from list(pool.imap())')2119    else:2120        raise AssertionError('expected ZeroDivisionError')21212122    it = pool.imap(f, list(range(10)))2123    for i in range(10):2124        try:2125            x = next(it)2126        except ZeroDivisionError:2127            if i == 5:2128                pass2129        except StopIteration:2130            break2131        else:2132            if i == 5:2133                raise AssertionError('expected ZeroDivisionError')21342135    assert i == 92136    print('\tGot ZeroDivisionError as expected from IMapIterator.next()')2137    print()21382139    #2140    # Testing timeouts2141    #21422143    print('Testing ApplyResult.get() with timeout:', end=' ')2144    res = pool.apply_async(calculate, TASKS[0])2145    while 1:2146        sys.stdout.flush()2147        try:2148            sys.stdout.write('\n\t%s' % res.get(0.02))2149            break2150        except multiprocessing.TimeoutError:2151            sys.stdout.write('.')2152    print()2153    print()21542155    print('Testing IMapIterator.next() with timeout:', end=' ')2156    it = pool.imap(calculatestar, TASKS)2157    while 1:2158        sys.stdout.flush()2159        try:2160            sys.stdout.write('\n\t%s' % it.next(0.02))2161        except StopIteration:2162            break2163        except multiprocessing.TimeoutError:2164            sys.stdout.write('.')2165    print()2166    print()21672168    #2169    # Testing callback2170    #21712172    print('Testing callback:')21732174    A = []2175    B = [56, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]21762177    r = pool.apply_async(mul, (7, 8), callback=A.append)2178    r.wait()21792180    r = pool.map_async(pow3, list(range(10)), callback=A.extend)2181    r.wait()21822183    if A == B:2184        print('\tcallbacks succeeded\n')2185    else:2186        print('\t*** callbacks failed\n\t\t%s != %s\n' % (A, B))21872188    #2189    # Check there are no outstanding tasks2190    #21912192    assert not pool._cache, 'cache = %r' % pool._cache21932194    #2195    # Check close() methods2196    #21972198    print('Testing close():')21992200    for worker in pool._pool:2201        assert worker.is_alive()22022203    result = pool.apply_async(time.sleep, [0.5])2204    pool.close()2205    pool.join()22062207    assert result.get() is None22082209    for worker in pool._pool:2210        assert not worker.is_alive()22112212    print('\tclose() succeeded\n')22132214    #2215    # Check terminate() method2216    #22172218    print('Testing terminate():')22192220    pool = multiprocessing.Pool(2)2221    DELTA = 0.12222    ignore = pool.apply(pow3, [2])2223    results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]2224    pool.terminate()2225    pool.join()22262227    for worker in pool._pool:2228        assert not worker.is_alive()22292230    print('\tterminate() succeeded\n')22312232    #2233    # Check garbage collection2234    #22352236    print('Testing garbage collection:')22372238    pool = multiprocessing.Pool(2)2239    DELTA = 0.12240    processes = pool._pool2241    ignore = pool.apply(pow3, [2])2242    results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]22432244    results = pool = None22452246    time.sleep(DELTA * 2)22472248    for worker in processes:2249        assert not worker.is_alive()22502251    print('\tgarbage collection succeeded\n')22522253if __name__ == '__main__':2254    multiprocessing.freeze_support()22552256    assert len(sys.argv) in (1, 2)22572258    if len(sys.argv) == 1 or sys.argv[1] == 'processes':2259        print(' Using processes '.center(79, '-'))2260    elif sys.argv[1] == 'threads':2261        print(' Using threads '.center(79, '-'))2262        import multiprocessing.dummy as multiprocessing2263    else:2264        print('Usage:\n\t%s [processes | threads]' % sys.argv[0])2265        raise SystemExit(2)22662267    test()2268```22692270Типы синхронизации, такие как блокировки, условия и очереди:22712272```python2273#2274# A test file for the `multiprocessing` package2275#2276# Copyright (c) 2006-2008, R Oudkerk2277# All rights reserved.2278#22792280import time, sys, random2281from queue import Empty22822283import multiprocessing               # may get overwritten22842285#### TEST_VALUE22862287def value_func(running, mutex):2288    random.seed()2289    time.sleep(random.random()*4)22902291    mutex.acquire()2292    print('\n\t\t\t' + str(multiprocessing.current_process()) + ' has finished')2293    running.value -= 12294    mutex.release()22952296def test_value():2297    TASKS = 102298    running = multiprocessing.Value('i', TASKS)2299    mutex = multiprocessing.Lock()23002301    for i in range(TASKS):2302        p = multiprocessing.Process(target=value_func, args=(running, mutex))2303        p.start()23042305    while running.value > 0:2306        time.sleep(0.08)2307        mutex.acquire()2308        print(running.value, end=' ')2309        sys.stdout.flush()2310        mutex.release()23112312    print()2313    print('No more running processes')23142315#### TEST_QUEUE23162317def queue_func(queue):2318    for i in range(30):2319        time.sleep(0.5 * random.random())2320        queue.put(i*i)2321    queue.put('STOP')23222323def test_queue():2324    q = multiprocessing.Queue()23252326    p = multiprocessing.Process(target=queue_func, args=(q,))2327    p.start()23282329    o = None2330    while o != 'STOP':2331        try:2332            o = q.get(timeout=0.3)2333            print(o, end=' ')2334            sys.stdout.flush()2335        except Empty:2336            print('TIMEOUT')23372338    print()23392340#### TEST_CONDITION23412342def condition_func(cond):2343    cond.acquire()2344    print('\t' + str(cond))2345    time.sleep(2)2346    print('\tchild is notifying')2347    print('\t' + str(cond))2348    cond.notify()2349    cond.release()23502351def test_condition():2352    cond = multiprocessing.Condition()23532354    p = multiprocessing.Process(target=condition_func, args=(cond,))2355    print(cond)23562357    cond.acquire()2358    print(cond)2359    cond.acquire()2360    print(cond)23612362    p.start()23632364    print('main is waiting')2365    cond.wait()2366    print('main has woken up')23672368    print(cond)2369    cond.release()2370    print(cond)2371    cond.release()23722373    p.join()2374    print(cond)23752376#### TEST_SEMAPHORE23772378def semaphore_func(sema, mutex, running):2379    sema.acquire()23802381    mutex.acquire()2382    running.value += 12383    print(running.value, 'tasks are running')2384    mutex.release()23852386    random.seed()2387    time.sleep(random.random()*2)23882389    mutex.acquire()2390    running.value -= 12391    print('%s has finished' % multiprocessing.current_process())2392    mutex.release()23932394    sema.release()23952396def test_semaphore():2397    sema = multiprocessing.Semaphore(3)2398    mutex = multiprocessing.RLock()2399    running = multiprocessing.Value('i', 0)24002401    processes = [2402        multiprocessing.Process(target=semaphore_func,2403                                args=(sema, mutex, running))2404        for i in range(10)2405        ]24062407    for p in processes:2408        p.start()24092410    for p in processes:2411        p.join()24122413#### TEST_JOIN_TIMEOUT24142415def join_timeout_func():2416    print('\tchild sleeping')2417    time.sleep(5.5)2418    print('\n\tchild terminating')24192420def test_join_timeout():2421    p = multiprocessing.Process(target=join_timeout_func)2422    p.start()24232424    print('waiting for process to finish')24252426    while 1:2427        p.join(timeout=1)2428        if not p.is_alive():2429            break2430        print('.', end=' ')2431        sys.stdout.flush()24322433#### TEST_EVENT24342435def event_func(event):2436    print('\t%r is waiting' % multiprocessing.current_process())2437    event.wait()2438    print('\t%r has woken up' % multiprocessing.current_process())24392440def test_event():2441    event = multiprocessing.Event()24422443    processes = [multiprocessing.Process(target=event_func, args=(event,))2444                 for i in range(5)]24452446    for p in processes:2447        p.start()24482449    print('main is sleeping')2450    time.sleep(2)24512452    print('main is setting event')2453    event.set()24542455    for p in processes:2456        p.join()24572458#### TEST_SHAREDVALUES24592460def sharedvalues_func(values, arrays, shared_values, shared_arrays):2461    for i in range(len(values)):2462        v = values[i][1]2463        sv = shared_values[i].value2464        assert v == sv24652466    for i in range(len(values)):2467        a = arrays[i][1]2468        sa = list(shared_arrays[i][:])2469        assert a == sa24702471    print('Tests passed')24722473def test_sharedvalues():2474    values = [2475        ('i', 10),2476        ('h', -2),2477        ('d', 1.25)2478        ]2479    arrays = [2480        ('i', list(range(100))),2481        ('d', [0.25 * i for i in range(100)]),2482        ('H', list(range(1000)))2483        ]24842485    shared_values = [multiprocessing.Value(id, v) for id, v in values]2486    shared_arrays = [multiprocessing.Array(id, a) for id, a in arrays]24872488    p = multiprocessing.Process(2489        target=sharedvalues_func,2490        args=(values, arrays, shared_values, shared_arrays)2491        )2492    p.start()2493    p.join()24942495    assert p.exitcode == 024962497####24982499def test(namespace=multiprocessing):2500    global multiprocessing25012502    multiprocessing = namespace25032504    for func in [ test_value, test_queue, test_condition,2505                  test_semaphore, test_join_timeout, test_event,2506                  test_sharedvalues ]:25072508        print('\n\t######## %s\n' % func.__name__)2509        func()25102511    ignore = multiprocessing.active_children()      # cleanup any old processes2512    if hasattr(multiprocessing, '_debug_info'):2513        info = multiprocessing._debug_info()2514        if info:2515            print(info)2516            raise ValueError('there should be no positive refcounts left')25172518if __name__ == '__main__':2519    multiprocessing.freeze_support()25202521    assert len(sys.argv) in (1, 2)25222523    if len(sys.argv) == 1 or sys.argv[1] == 'processes':2524        print(' Using processes '.center(79, '-'))2525        namespace = multiprocessing2526    elif sys.argv[1] == 'manager':2527        print(' Using processes and a manager '.center(79, '-'))2528        namespace = multiprocessing.Manager()2529        namespace.Process = multiprocessing.Process2530        namespace.current_process = multiprocessing.current_process2531        namespace.active_children = multiprocessing.active_children2532    elif sys.argv[1] == 'threads':2533        print(' Using threads '.center(79, '-'))2534        import multiprocessing.dummy as namespace2535    else:2536        print('Usage:\n\t%s [processes | manager | threads]' % sys.argv[0])2537        raise SystemExit(2)25382539    test(namespace)2540```25412542Пример, показывающий, как использовать очереди для передачи задач набору рабочих процессов и сбора результатов:25432544```python2545#2546# Простой пример использования пула рабочих процессов для выполнения задач.2547#2548# Обратите внимание, что результаты, скорее всего, не будут получены из выходной2549# очереди в том же порядке, в котором соответствующие задачи были2550# помещены во входную очередь. Если важно получить результаты2551# в исходном порядке, рассмотрите использование `Pool.map()` или2552# `Pool.imap()` (что в любом случае сократит объём необходимого кода).2553#2554# Авторское право (c) 2006-2008, R Oudkerk2555# Все права защищены.2556#25572558import time2559import random25602561from multiprocessing import Process, Queue, current_process, freeze_support25622563#2564# Функция, выполняемая рабочими процессами2565#25662567def worker(input, output):2568    for func, args in iter(input.get, 'STOP'):2569        result = calculate(func, args)2570        output.put(result)25712572#2573# Функция, используемая для вычисления результата2574#25752576def calculate(func, args):2577    result = func(*args)2578    return '%s says that %s%s = %s' % \2579        (current_process().name, func.__name__, args, result)25802581#2582# Функции, на которые ссылаются задачи2583#25842585def mul(a, b):2586    time.sleep(0.5*random.random())2587    return a * b25882589def plus(a, b):2590    time.sleep(0.5*random.random())2591    return a + b25922593#2594#2595#25962597def test():2598    NUMBER_OF_PROCESSES = 42599    TASKS1 = [(mul, (i, 7)) for i in range(20)]2600    TASKS2 = [(plus, (i, 8)) for i in range(10)]26012602    # Создание очередей2603    task_queue = Queue()2604    done_queue = Queue()26052606    # Отправка задач2607    for task in TASKS1:2608        task_queue.put(task)26092610    # Запуск рабочих процессов2611    for i in range(NUMBER_OF_PROCESSES):2612        Process(target=worker, args=(task_queue, done_queue)).start()26132614    # Получение и вывод результатов2615    print('Unordered results:')2616    for i in range(len(TASKS1)):2617        print('\t', done_queue.get())26182619    # Добавить ещё задач с помощью `put()`2620    for task in TASKS2:2621        task_queue.put(task)26222623    # Получить и вывести ещё несколько результатов2624    for i in range(len(TASKS2)):2625        print('\t', done_queue.get())26262627    # Сообщить дочерним процессам об остановке2628    for i in range(NUMBER_OF_PROCESSES):2629        task_queue.put('STOP')26302631if __name__ == '__main__':2632    freeze_support()2633    test()2634```26352636Пример того, как пул рабочих процессов может каждый запустить экземпляр `SimpleHTTPServer.HttpServer`, разделяя один прослушивающий сокет.26372638```python2639#2640# Пример, в котором пул HTTP-серверов использует один общий слушающий сокет2641#2642# В Windows этот модуль зависит от возможности сериализации сокета с помощью pickle2643# объекта, чтобы рабочие процессы могли унаследовать копию сервера2644# объект. (Мы импортируем `multiprocessing.reduction`, чтобы включить эту сериализацию.)2645#2646# Не уверены, нужно ли синхронизировать доступ к методу `socket.accept()` с помощью2647# блокировки, разделяемой между процессами – похоже, это не обязательно.2648#2649# Авторское право (c) 2006-2008, R Oudkerk2650# Все права защищены.2651#26522653import os2654import sys26552656from multiprocessing import Process, current_process, freeze_support2657from http.server import HTTPServer2658from http.server import SimpleHTTPRequestHandler26592660if sys.platform == 'win32':2661    import multiprocessing.reduction    # сделать сокеты сериализуемыми/наследуемыми26622663def note(format, *args):2664    sys.stderr.write('[%s]\t%s\n' % (current_process().name, format%args))26652666class RequestHandler(SimpleHTTPRequestHandler):2667    # мы переопределяем log_message(), чтобы показывать, какой процесс обрабатывает запрос2668    def log_message(self, format, *args):2669        note(format, *args)26702671def serve_forever(server):2672    note('starting server')2673    try:2674        server.serve_forever()2675    except KeyboardInterrupt:2676        pass26772678def runpool(address, number_of_processes):2679    # создать единственный объект сервера – каждый дочерний процесс унаследует его копию2680    server = HTTPServer(address, RequestHandler)26812682    # создать дочерние процессы, которые будут работать как воркеры2683    for i in range(number_of_processes-1):2684        Process(target=serve_forever, args=(server,)).start()26852686    # главный процесс также выступает в роли воркера2687    serve_forever(server)26882689def test():2690    DIR = os.path.join(os.path.dirname(__file__), '..')2691    ADDRESS = ('localhost', 8000)2692    NUMBER_OF_PROCESSES = 426932694    print('Serving at http://%s:%d using %d worker processes' % \2695          (ADDRESS[0], ADDRESS[1], NUMBER_OF_PROCESSES))2696    print('To exit press Ctrl-' + ['C', 'Break'][sys.platform=='win32'])26972698    os.chdir(DIR)2699    runpool(ADDRESS, NUMBER_OF_PROCESSES)27002701if __name__ == '__main__':2702    freeze_support()2703    test()2704```27052706Некоторые простые тесты производительности, сравнивающие `multiprocessing` с [`threading`](https://python-all.ru/3.0/library/threading.html#module-threading):27072708```python2709#2710# Простые тесты производительности для пакета multiprocessing2711#2712# Авторское право (c) 2006-2008, R Oudkerk2713# Все права защищены.2714#27152716import time, sys, multiprocessing, threading, queue, gc27172718if sys.platform == 'win32':2719    _timer = time.clock2720else:2721    _timer = time.time27222723delta = 127242725#### TEST_QUEUESPEED27262727def queuespeed_func(q, c, iterations):2728    a = '0' * 2562729    c.acquire()2730    c.notify()2731    c.release()27322733    for i in range(iterations):2734        q.put(a)27352736    q.put('STOP')27372738def test_queuespeed(Process, q, c):2739    elapsed = 02740    iterations = 127412742    while elapsed < delta:2743        iterations *= 227442745        p = Process(target=queuespeed_func, args=(q, c, iterations))2746        c.acquire()2747        p.start()2748        c.wait()2749        c.release()27502751        result = None2752        t = _timer()27532754        while result != 'STOP':2755            result = q.get()27562757        elapsed = _timer() - t27582759        p.join()27602761    print(iterations, 'objects passed through the queue in', elapsed, 'seconds')2762    print('average number/sec:', iterations/elapsed)27632764#### TEST_PIPESPEED27652766def pipe_func(c, cond, iterations):2767    a = '0' * 2562768    cond.acquire()2769    cond.notify()2770    cond.release()27712772    for i in range(iterations):2773        c.send(a)27742775    c.send('STOP')27762777def test_pipespeed():2778    c, d = multiprocessing.Pipe()2779    cond = multiprocessing.Condition()2780    elapsed = 02781    iterations = 127822783    while elapsed < delta:2784        iterations *= 227852786        p = multiprocessing.Process(target=pipe_func,2787                                    args=(d, cond, iterations))2788        cond.acquire()2789        p.start()2790        cond.wait()2791        cond.release()27922793        result = None2794        t = _timer()27952796        while result != 'STOP':2797            result = c.recv()27982799        elapsed = _timer() - t2800        p.join()28012802    print(iterations, 'objects passed through connection in',elapsed,'seconds')2803    print('average number/sec:', iterations/elapsed)28042805#### TEST_SEQSPEED28062807def test_seqspeed(seq):2808    elapsed = 02809    iterations = 128102811    while elapsed < delta:2812        iterations *= 228132814        t = _timer()28152816        for i in range(iterations):2817            a = seq[5]28182819        elapsed = _timer()-t28202821    print(iterations, 'iterations in', elapsed, 'seconds')2822    print('average number/sec:', iterations/elapsed)28232824#### TEST_LOCK28252826def test_lockspeed(l):2827    elapsed = 02828    iterations = 128292830    while elapsed < delta:2831        iterations *= 228322833        t = _timer()28342835        for i in range(iterations):2836            l.acquire()2837            l.release()28382839        elapsed = _timer()-t28402841    print(iterations, 'iterations in', elapsed, 'seconds')2842    print('average number/sec:', iterations/elapsed)28432844#### TEST_CONDITION28452846def conditionspeed_func(c, N):2847    c.acquire()2848    c.notify()28492850    for i in range(N):2851        c.wait()2852        c.notify()28532854    c.release()28552856def test_conditionspeed(Process, c):2857    elapsed = 02858    iterations = 128592860    while elapsed < delta:2861        iterations *= 228622863        c.acquire()2864        p = Process(target=conditionspeed_func, args=(c, iterations))2865        p.start()28662867        c.wait()28682869        t = _timer()28702871        for i in range(iterations):2872            c.notify()2873            c.wait()28742875        elapsed = _timer()-t28762877        c.release()2878        p.join()28792880    print(iterations * 2, 'waits in', elapsed, 'seconds')2881    print('average number/sec:', iterations * 2 / elapsed)28822883####28842885def test():2886    manager = multiprocessing.Manager()28872888    gc.disable()28892890    print('\n\t######## testing Queue.Queue\n')2891    test_queuespeed(threading.Thread, queue.Queue(),2892                    threading.Condition())2893    print('\n\t######## testing multiprocessing.Queue\n')2894    test_queuespeed(multiprocessing.Process, multiprocessing.Queue(),2895                    multiprocessing.Condition())2896    print('\n\t######## testing Queue managed by server process\n')2897    test_queuespeed(multiprocessing.Process, manager.Queue(),2898                    manager.Condition())2899    print('\n\t######## testing multiprocessing.Pipe\n')2900    test_pipespeed()29012902    print()29032904    print('\n\t######## testing list\n')2905    test_seqspeed(list(range(10)))2906    print('\n\t######## testing list managed by server process\n')2907    test_seqspeed(manager.list(list(range(10))))2908    print('\n\t######## testing Array("i", ..., lock=False)\n')2909    test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=False))2910    print('\n\t######## testing Array("i", ..., lock=True)\n')2911    test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=True))29122913    print()29142915    print('\n\t######## testing threading.Lock\n')2916    test_lockspeed(threading.Lock())2917    print('\n\t######## testing threading.RLock\n')2918    test_lockspeed(threading.RLock())2919    print('\n\t######## testing multiprocessing.Lock\n')2920    test_lockspeed(multiprocessing.Lock())2921    print('\n\t######## testing multiprocessing.RLock\n')2922    test_lockspeed(multiprocessing.RLock())2923    print('\n\t######## testing lock managed by server process\n')2924    test_lockspeed(manager.Lock())2925    print('\n\t######## testing rlock managed by server process\n')2926    test_lockspeed(manager.RLock())29272928    print()29292930    print('\n\t######## testing threading.Condition\n')2931    test_conditionspeed(threading.Thread, threading.Condition())2932    print('\n\t######## testing multiprocessing.Condition\n')2933    test_conditionspeed(multiprocessing.Process, multiprocessing.Condition())2934    print('\n\t######## testing condition managed by a server process\n')2935    test_conditionspeed(multiprocessing.Process, manager.Condition())29362937    gc.enable()29382939if __name__ == '__main__':2940    multiprocessing.freeze_support()2941    test()2942```29432944Пример/демонстрация того, как использовать `managers.SyncManager`, `Process` и другие для построения системы, которая может распределять процессы и работу через распределенную очередь на «кластер» машин в сети, доступный по SSH. Для этого потребуется настроить аутентификацию по закрытому ключу для всех хостов.29452946```python2947#2948# Модуль для запуска процессов на удалённом хосте2949#2950# Зависит от пакета `multiprocessing` – тестировалось с `processing-0.60`2951#2952# Авторское право (c) 2006-2008, R Oudkerk2953# Все права защищены.2954#29552956__all__ = ['Cluster', 'Host', 'get_logger', 'current_process']29572958#2959# Импорты2960#29612962import sys2963import os2964import tarfile2965import shutil2966import subprocess2967import logging2968import itertools2969import queue29702971try:2972    import pickle as pickle2973except ImportError:2974    import pickle29752976from multiprocessing import Process, current_process, cpu_count2977from multiprocessing import util, managers, connection, forking, pool29782979#2980# Логирование2981#29822983def get_logger():2984    return _logger29852986_logger = logging.getLogger('distributing')2987_logger.propogate = 029882989_formatter = logging.Formatter(util.DEFAULT_LOGGING_FORMAT)2990_handler = logging.StreamHandler()2991_handler.setFormatter(_formatter)2992_logger.addHandler(_handler)29932994info = _logger.info2995debug = _logger.debug29962997#2998# Получить количество процессоров2999#30003001try:3002    slot_count = cpu_count()3003except NotImplemented:3004    slot_count = 130053006#3007# Тип менеджера, который порождает подпроцессы3008#30093010class HostManager(managers.SyncManager):3011    '''3012    Тип менеджера, используемый для запуска процессов на (предположительно) удалённом хосте3013    '''3014    def __init__(self, address, authkey):3015        managers.SyncManager.__init__(self, address, authkey)3016        self._name = 'Host-unknown'30173018    def Process(self, group=None, target=None, name=None, args=(), kwargs={}):3019        if hasattr(sys.modules['__main__'], '__file__'):3020            main_path = os.path.basename(sys.modules['__main__'].__file__)3021        else:3022            main_path = None3023        data = pickle.dumps((target, args, kwargs))3024        p = self._RemoteProcess(data, main_path)3025        if name is None:3026            temp = self._name.split('Host-')[-1] + '/Process-%s'3027            name = temp % ':'.join(map(str, p.get_identity()))3028        p.set_name(name)3029        return p30303031    @classmethod3032    def from_address(cls, address, authkey):3033        manager = cls(address, authkey)3034        managers.transact(address, authkey, 'dummy')3035        manager._state.value = managers.State.STARTED3036        manager._name = 'Host-%s:%s' % manager.address3037        manager.shutdown = util.Finalize(3038            manager, HostManager._finalize_host,3039            args=(manager._address, manager._authkey, manager._name),3040            exitpriority=-103041            )3042        return manager30433044    @staticmethod3045    def _finalize_host(address, authkey, name):3046        managers.transact(address, authkey, 'shutdown')30473048    def __repr__(self):3049        return '<Host(%s)>' % self._name30503051#3052# Подкласс Process, представляющий процесс на (возможно) удалённой машине3053#30543055class RemoteProcess(Process):3056    '''3057    Представляет процесс, запущенный на удалённом хосте3058    '''3059    def __init__(self, data, main_path):3060        assert not main_path or os.path.basename(main_path) == main_path3061        Process.__init__(self)3062        self._data = data3063        self._main_path = main_path30643065    def _bootstrap(self):3066        forking.prepare({'main_path': self._main_path})3067        self._target, self._args, self._kwargs = pickle.loads(self._data)3068        return Process._bootstrap(self)30693070    def get_identity(self):3071        return self._identity30723073HostManager.register('_RemoteProcess', RemoteProcess)30743075#3076# Класс Pool, использующий кластер3077#30783079class DistributedPool(pool.Pool):30803081    def __init__(self, cluster, processes=None, initializer=None, initargs=()):3082        self._cluster = cluster3083        self.Process = cluster.Process3084        pool.Pool.__init__(self, processes or len(cluster),3085                           initializer, initargs)30863087    def _setup_queues(self):3088        self._inqueue = self._cluster._SettableQueue()3089        self._outqueue = self._cluster._SettableQueue()3090        self._quick_put = self._inqueue.put3091        self._quick_get = self._outqueue.get30923093    @staticmethod3094    def _help_stuff_finish(inqueue, task_handler, size):3095        inqueue.set_contents([None] * size)30963097#3098# Тип менеджера, который запускает менеджеры хостов на других машинах3099#31003101def LocalProcess(**kwds):3102    p = Process(**kwds)3103    p.set_name('localhost/' + p.name)3104    return p31053106class Cluster(managers.SyncManager):3107    '''3108    Представляет набор слотов, работающих на разных хостах31093110    `Cluster` является подклассом `SyncManager`, поэтому он позволяет создавать3111    различные типы разделяемых объектов.3112    '''3113    def __init__(self, hostlist, modules):3114        managers.SyncManager.__init__(self, address=('localhost', 0))3115        self._hostlist = hostlist3116        self._modules = modules3117        if __name__ not in modules:3118            modules.append(__name__)3119        files = [sys.modules[name].__file__ for name in modules]3120        for i, file in enumerate(files):3121            if file.endswith('.pyc') or file.endswith('.pyo'):3122                files[i] = file[:-4] + '.py'3123        self._files = [os.path.abspath(file) for file in files]31243125    def start(self):3126        managers.SyncManager.start(self)31273128        l = connection.Listener(family='AF_INET', authkey=self._authkey)31293130        for i, host in enumerate(self._hostlist):3131            host._start_manager(i, self._authkey, l.address, self._files)31323133        for host in self._hostlist:3134            if host.hostname != 'localhost':3135                conn = l.accept()3136                i, address, cpus = conn.recv()3137                conn.close()3138                other_host = self._hostlist[i]3139                other_host.manager = HostManager.from_address(address,3140                                                              self._authkey)3141                other_host.slots = other_host.slots or cpus3142                other_host.Process = other_host.manager.Process3143            else:3144                host.slots = host.slots or slot_count3145                host.Process = LocalProcess31463147        self._slotlist = [3148            Slot(host) for host in self._hostlist for i in range(host.slots)3149            ]3150        self._slot_iterator = itertools.cycle(self._slotlist)3151        self._base_shutdown = self.shutdown3152        del self.shutdown31533154    def shutdown(self):3155        for host in self._hostlist:3156            if host.hostname != 'localhost':3157                host.manager.shutdown()3158        self._base_shutdown()31593160    def Process(self, group=None, target=None, name=None, args=(), kwargs={}):3161        slot = next(self._slot_iterator)3162        return slot.Process(3163            group=group, target=target, name=name, args=args, kwargs=kwargs3164            )31653166    def Pool(self, processes=None, initializer=None, initargs=()):3167        return DistributedPool(self, processes, initializer, initargs)31683169    def __getitem__(self, i):3170        return self._slotlist[i]31713172    def __len__(self):3173        return len(self._slotlist)31743175    def __iter__(self):3176        return iter(self._slotlist)31773178#3179# Подкласс Queue, используемый распределённым пулом3180#31813182class SettableQueue(queue.Queue):3183    def empty(self):3184        return not self.queue3185    def full(self):3186        return self.maxsize > 0 and len(self.queue) == self.maxsize3187    def set_contents(self, contents):3188        # длина содержимого должна быть не меньше количества3189        # потоков, которые потенциально вызвали get()3190        self.not_empty.acquire()3191        try:3192            self.queue.clear()3193            self.queue.extend(contents)3194            self.not_empty.notifyAll()3195        finally:3196            self.not_empty.release()31973198Cluster.register('_SettableQueue', SettableQueue)31993200#3201# Класс, представляющий логический процессор в кластере3202#32033204class Slot(object):3205    def __init__(self, host):3206        self.host = host3207        self.Process = host.Process32083209#3210# Хост3211#32123213class Host(object):3214    '''3215    Представляет хост, используемый как узел в кластере.32163217    `hostname` задаёт имя хоста. Если hostname не3218    то для входа на хост используется ssh. Для входа под3219    от имени другого пользователя используйте имя хоста вида3220    "username@somewhere.org"32213222    `slots` используется для указания количества слотов для процессов на3223    хосте. Это влияет на то, как часто процессы будут распределяться по3224    этому хосту. Обычно это должно быть равно количеству процессоров на3225    том хосте.3226    '''3227    def __init__(self, hostname, slots=None):3228        self.hostname = hostname3229        self.slots = slots32303231    def _start_manager(self, index, authkey, address, files):3232        if self.hostname != 'localhost':3233            tempdir = copy_to_remote_temporary_directory(self.hostname, files)3234            debug('startup files copied to %s:%s', self.hostname, tempdir)3235            p = subprocess.Popen(3236                ['ssh', self.hostname, 'python', '-c',3237                 '"import os; os.chdir(%r); '3238                 'from distributing import main; main()"' % tempdir],3239                stdin=subprocess.PIPE3240                )3241            data = dict(3242                name='BoostrappingHost', index=index,3243                dist_log_level=_logger.getEffectiveLevel(),3244                dir=tempdir, authkey=str(authkey), parent_address=address3245                )3246            pickle.dump(data, p.stdin, pickle.HIGHEST_PROTOCOL)3247            p.stdin.close()32483249#3250# Копирует файлы в удалённый каталог, возвращая имя каталога.3251#32523253unzip_code = '''"3254import tempfile, os, sys, tarfile3255tempdir = tempfile.mkdtemp(prefix='distrib-')3256os.chdir(tempdir)3257tf = tarfile.open(fileobj=sys.stdin, mode='r|gz')3258for ti in tf:3259    tf.extract(ti)3260print tempdir3261"'''32623263def copy_to_remote_temporary_directory(host, files):3264    p = subprocess.Popen(3265        ['ssh', host, 'python', '-c', unzip_code],3266        stdout=subprocess.PIPE, stdin=subprocess.PIPE3267        )3268    tf = tarfile.open(fileobj=p.stdin, mode='w|gz')3269    for name in files:3270        tf.add(name, os.path.basename(name))3271    tf.close()3272    p.stdin.close()3273    return p.stdout.read().rstrip()32743275#3276# Код, который запускает менеджер хостов.3277#32783279def main():3280    # Получает данные от родительского процесса через stdin.3281    data = pickle.load(sys.stdin)3282    sys.stdin.close()32833284    # Устанавливает некоторые параметры.3285    _logger.setLevel(data['dist_log_level'])3286    forking.prepare(data)32873288    # Создаёт сервер для объекта `HostManager`.3289    server = managers.Server(HostManager._registry, ('', 0), data['authkey'])3290    current_process()._server = server32913292    # Сообщает родительскому процессу адрес сервера и количество процессоров.3293    conn = connection.Client(data['parent_address'], authkey=data['authkey'])3294    conn.send((data['index'], server.address, slot_count))3295    conn.close()32963297    # Устанавливает имя и т.д.3298    current_process().set_name('Host-%s:%s' % server.address)3299    util._run_after_forkers()33003301    # Регистрирует функцию очистки.3302    def cleanup(directory):3303        debug('removing directory %s', directory)3304        shutil.rmtree(directory)3305        debug('shutting down host manager')3306    util.Finalize(None, cleanup, args=[data['dir']], exitpriority=0)33073308    # Запускает менеджер хостов.3309    debug('remote host manager starting in %s', data['dir'])3310    server.serve_forever()3311```3312