multiprocessing.md
1> **Источник:** https://python-all.ru/3.3/library/multiprocessing.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 17.2. [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) – Процессно-ориентированный параллелизм89## 17.2.1. Введение1011Пакет [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) поддерживает порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/3.3/library/threading.html#module-threading). Пакет [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) предоставляет как локальный, так и удалённый параллелизм, обходя [*Глобальную блокировку интерпретатора*](https://python-all.ru/3.3/glossary.html#term-global-interpreter-lock) с помощью подпроцессов вместо потоков. Благодаря этому модуль [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) позволяет программисту полностью задействовать несколько процессоров на данной машине. Он работает как на Unix, так и на Windows.1213> **Примечание**14>15> Часть функционала этого пакета требует работающей реализации разделяемого семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки его импорта приведут к [`ImportError`](https://python-all.ru/3.3/library/exceptions.html#ImportError). Дополнительную информацию см. в [issue 3770](https://python-all.ru/3.3/library/multiprocessing.html).1617> **Примечание**18>19> Для работы некоторых функций этого пакета требуется, чтобы модуль `__main__` был импортируем дочерними процессами. Это рассмотрено в разделе [*Рекомендации по программированию*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-programming), но стоит упомянуть здесь. Это означает, что некоторые примеры, например, с [`multiprocessing.pool.Pool`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.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> Process PoolWorker-3:31> Traceback (most recent call last):32> AttributeError: 'module' object has no attribute 'f'33> AttributeError: 'module' object has no attribute 'f'34> AttributeError: 'module' object has no attribute 'f'35> ```36>37> (Если попробовать это сделать, на самом деле будут выведены три полные трассировки стека, перемешанные в полу-случайном порядке, и затем, возможно, придётся как-то остановить главный процесс.)3839### 17.2.1.1. Класс [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process)4041В [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) процессы порождаются созданием объекта [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.start). [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process) следует API [`threading.Thread`](https://python-all.ru/3.3/library/threading.html#threading.Thread). Пример простой многопроцессной программы:4243```python44from multiprocessing import Process4546def f(name):47 print('hello', name)4849if __name__ == '__main__':50 p = Process(target=f, args=('bob',))51 p.start()52 p.join()53```5455Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:5657```python58from multiprocessing import Process59import os6061def info(title):62 print(title)63 print('module name:', __name__)64 if hasattr(os, 'getppid'): # доступно только на Unix65 print('parent process:', os.getppid())66 print('process id:', os.getpid())6768def f(name):69 info('function f')70 print('hello', name)7172if __name__ == '__main__':73 info('main line')74 p = Process(target=f, args=('bob',))75 p.start()76 p.join()77```7879Зачем (в Windows) нужна конструкция `if __name__ == '__main__'`, объясняется в разделе [*Рекомендации по программированию*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-programming).8081### 17.2.1.2. Обмен объектами между процессами8283[`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) поддерживает два типа каналов связи между процессами:8485**Очереди**8687> Класс [`Queue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue) является почти копией [`queue.Queue`](https://python-all.ru/3.3/library/queue.html#queue.Queue). Например:88>89> ```python90> from multiprocessing import Process, Queue91>92> def f(q):93> q.put([42, None, 'hello'])94>95> if __name__ == '__main__':96> q = Queue()97> p = Process(target=f, args=(q,))98> p.start()99> print(q.get()) # печатает "[42, None, 'hello']"100> p.join()101> ```102>103> Очереди потокобезопасны и безопасны для процессов.104105**Каналы**106107> Функция [`Pipe()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является двунаправленным. Например:108>109> ```python110> from multiprocessing import Process, Pipe111>112> def f(conn):113> conn.send([42, None, 'hello'])114> conn.close()115>116> if __name__ == '__main__':117> parent_conn, child_conn = Pipe()118> p = Process(target=f, args=(child_conn,))119> p.start()120> print(parent_conn.recv()) # печатает "[42, None, 'hello']"121> p.join()122> ```123>124> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы [`send()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.send) и [`recv()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.recv) (среди прочих). Следует учитывать, что данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.125126### 17.2.1.3. Синхронизация между процессами127128[`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) содержит эквиваленты всех примитивов синхронизации из [`threading`](https://python-all.ru/3.3/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс одновременно выводит на стандартный вывод:129130```python131from multiprocessing import Process, Lock132133def f(l, i):134 l.acquire()135 print('hello world', i)136 l.release()137138if __name__ == '__main__':139 lock = Lock()140141 for num in range(10):142 Process(target=f, args=(lock, num)).start()143```144145Без использования блокировки вывод от разных процессов может полностью перепутаться.146147### 17.2.1.4. Совместное использование состояния между процессами148149Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.150151Однако, если действительно необходимо использовать общие данные, [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) предоставляет несколько способов это сделать.152153**Разделяемая память**154155> Данные могут храниться в карте разделяемой памяти с помощью [`Value`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Array). Например, следующий код156>157> ```python158> from multiprocessing import Process, Value, Array159>160> def f(n, a):161> n.value = 3.1415927162> for i in range(len(a)):163> a[i] = -a[i]164>165> if __name__ == '__main__':166> num = Value('d', 0.0)167> arr = Array('i', range(10))168>169> p = Process(target=f, args=(num, arr))170> p.start()171> p.join()172>173> print(num.value)174> print(arr[:])175> ```176>177> выведет178>179> ```python180> 3.1415927181> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]182> ```183>184> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, как в модуле [`array`](https://python-all.ru/3.3/library/array.html#module-array): `'d'` обозначает число двойной точности с плавающей точкой, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасны для работы с процессами и потоками.185>186> Для большей гибкости при использовании разделяемой памяти можно воспользоваться модулем [`multiprocessing.sharedctypes`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing.sharedctypes), который поддерживает создание произвольных объектов ctypes, размещённых в разделяемой памяти.187188**Серверный процесс**189190> Объект менеджера, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими через прокси.191>192> Менеджер, возвращаемый `Manager()`, поддерживает типы [`list`](https://python-all.ru/3.3/library/stdtypes.html#list), [`dict`](https://python-all.ru/3.3/library/stdtypes.html#dict), `Namespace`, [`Блокировка`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Event), [`Barrier`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Barrier), [`Очередь`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Array). Например,193>194> ```python195> from multiprocessing import Process, Manager196>197> def f(d, l):198> d[1] = '1'199> d['2'] = 2200> d[0.25] = None201> l.reverse()202>203> if __name__ == '__main__':204> with Manager() as manager:205> d = manager.dict()206> l = manager.list(range(10))207>208> p = Process(target=f, args=(d, l))209> p.start()210> p.join()211>212> print(d)213> print(l)214> ```215>216> выведет217>218> ```python219> {0.25: None, 1: '1', '2': 2}220> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]221> ```222>223> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.224225### 17.2.1.5. Использование пула рабочих процессов226227Класс [`Pool`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool) представляет собой пул рабочих процессов. Он имеет методы, которые позволяют передавать задачи рабочим процессам разными способами.228229Например:230231```python232from multiprocessing import Pool233234def f(x):235 return x*x236237if __name__ == '__main__':238 with Pool(processes=4) as pool: # запустить 4 рабочих процесса239 result = pool.apply_async(f, [10]) # асинхронно вычислить "f(10)"240 print(result.get(timeout=1)) # выводит "100", если компьютер не *очень* медленный241 print(pool.map(f, range(10))) # выводит "[0, 1, 4,..., 81]"242```243244Обратите внимание: методы пула должны использоваться только тем процессом, который его создал.245246## 17.2.2. Справочник247248Пакет [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) в основном повторяет API модуля [`threading`](https://python-all.ru/3.3/library/threading.html#module-threading).249250### 17.2.2.1. [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process) и исключения251252#### `class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)`253254Объекты Process представляют активность, которая выполняется в отдельном процессе. Класс [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process) содержит эквиваленты всех методов [`threading.Thread`](https://python-all.ru/3.3/library/threading.html#threading.Thread).255256Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должно быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/3.3/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию равен `None`, то есть ничего не вызывается. *name* – имя процесса (подробнее см. [`name`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.name)). *args* – кортеж аргументов для вызова целевой функции. *kwargs* – это словарь именованных аргументов для вызова целевой функции. Если указан, ключевой аргумент *daemon* устанавливает флаг процесса [`daemon`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.daemon) в `True` или `False`. Если `None` (по умолчанию), этот флаг будет наследоваться от создающего процесса.257258По умолчанию в *target* не передаётся никаких аргументов.259260Если подкласс переопределяет конструктор, он должен убедиться, что вызывает конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.261262Изменено в версии 3.3: Добавлен аргумент *daemon*.263264#### `run()`265266Метод, представляющий активность процесса.267268Вы можете переопределить этот метод в подклассе. Стандартный метод [`run()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.269270#### `start()`271272Запускает активность процесса.273274Этот метод должен вызываться не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.275276#### `join([timeout])`277278Если необязательный аргумент *timeout* равен `None` (по умолчанию), метод блокируется до завершения процесса, для которого вызывается метод [`join()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.join). Если *timeout* – положительное число, блокировка длится не более *timeout* секунд.279280Процесс может быть присоединён много раз.281282Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.283284#### `name`285286Имя процесса. Имя – это строка, используемая только для идентификации. Оно не имеет семантического значения. Несколько процессов могут иметь одно и то же имя.287288Начальное имя задается конструктором. Если конструктору не передано явное имя, создается имя вида ‘Process-N1:N2:...:Nk‘, где каждый Nk – это N-й потомок своего родителя.289290#### `is_alive()`291292Возвращает, жив ли процесс.293294Грубо говоря, объект процесса считается живым с момента возврата метода [`start()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.295296#### `daemon`297298Флаг daemon процесса, логическое значение. Должен быть установлен до вызова [`start()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.start).299300Начальное значение наследуется от создающего процесса.301302Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.303304Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.305306В дополнение к API [`threading.Thread`](https://python-all.ru/3.3/library/threading.html#threading.Thread), объекты [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process) также поддерживают следующие атрибуты и методы:307308#### `pid`309310Возвращает идентификатор процесса. До запуска процесса будет `None`.311312#### `exitcode`313314Код завершения дочернего процесса. Будет `None`, если процесс ещё не завершился. Отрицательное значение *-N* указывает, что дочерний процесс был завершён сигналом *N*.315316#### `authkey`317318Ключ аутентификации процесса (байтовая строка).319320При инициализации [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) главному процессу присваивается случайная строка с помощью [`os.urandom()`](https://python-all.ru/3.3/library/os.html#os.urandom).321322При создании объекта [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process) он наследует ключ аутентификации своего родительского процесса, хотя это можно изменить, установив [`authkey`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.authkey) в другую строку байтов.323324См. [*Ключи аутентификации*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-auth-keys).325326#### `sentinel`327328Числовой дескриптор системного объекта, который становится «готов» по завершении процесса.329330Это значение можно использовать, если нужно ожидать несколько событий одновременно с помощью [`multiprocessing.connection.wait()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.connection.wait). В противном случае проще вызвать [`join()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.join).331332В Windows это дескриптор ОС, используемый с семейством API-вызовов `WaitForSingleObject` и `WaitForMultipleObjects`. В Unix это файловый дескриптор, используемый с примитивами из модуля [`select`](https://python-all.ru/3.3/library/select.html#module-select).333334Новое в версии 3.3.335336#### `terminate()`337338Завершает процесс. В Unix для этого используется сигнал `SIGTERM`; в Windows – `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.п. выполнены не будут.339340Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.341342> **Предупреждение**343>344> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.345346Обратите внимание, что методы [`start()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.is_alive), [`terminate()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.terminate) и [`exitcode`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.exitcode) должны вызываться только процессом, который создал объект процесса.347348Пример использования некоторых методов [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process):349350```python351>>> import multiprocessing, time, signal352>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))353>>> print(p, p.is_alive())354<Process(Process-1, initial)> False355>>> p.start()356>>> print(p, p.is_alive())357<Process(Process-1, started)> True358>>> p.terminate()359>>> time.sleep(0.1)360>>> print(p, p.is_alive())361<Process(Process-1, stopped[SIGTERM])> False362>>> p.exitcode == -signal.SIGTERM363True364```365366#### `exception multiprocessing.ProcessError`367368Базовый класс всех исключений [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing).369370#### `exception multiprocessing.BufferTooShort`371372Исключение, вызываемое [`Connection.recv_bytes_into()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.recv_bytes_into), когда переданный объект буфера слишком мал для читаемого сообщения.373374Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде строки байтов.375376#### `exception multiprocessing.AuthenticationError`377378Возбуждается при ошибке аутентификации.379380#### `exception multiprocessing.TimeoutError`381382Возбуждается методами с тайм-аутом по истечении тайм-аута.383384### 17.2.2.2. Каналы и очереди385386При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.387388Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает множество производителей и потребителей).389390Типы [`Queue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue), [`SimpleQueue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.SimpleQueue) и [`JoinableQueue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue) представляют собой очереди FIFO со многими производителями и потребителями, построенные по образцу класса [`queue.Queue`](https://python-all.ru/3.3/library/queue.html#queue.Queue) из стандартной библиотеки. Они отличаются тем, что [`Queue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue) не имеет методов [`task_done()`](https://python-all.ru/3.3/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.3/library/queue.html#queue.Queue.join), появившихся в классе [`queue.Queue`](https://python-all.ru/3.3/library/queue.html#queue.Queue) из Python 2.5.391392Если используется [`JoinableQueue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue), то **необходимо** вызывать [`JoinableQueue.task_done()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди, иначе семафор, используемый для подсчёта количества незавершённых задач, может в конечном счёте переполниться, что приведёт к возникновению исключения.393394Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [*Менеджеры*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-managers).395396> **Примечание**397>398> [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) использует стандартные исключения [`queue.Empty`](https://python-all.ru/3.3/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.3/library/queue.html#queue.Full) для сигнализации о тайм-ауте. Они недоступны в пространстве имён [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing), поэтому их необходимо импортировать из [`queue`](https://python-all.ru/3.3/library/queue.html#module-queue).399400> **Примечание**401>402> Когда объект помещается в очередь, он сериализуется с помощью модуля pickle, и фоновый поток позднее сбрасывает сериализованные данные в нижележащий канал. Это имеет некоторые неожиданные последствия, но обычно не вызывает практических трудностей – если это действительно беспокоит, можно вместо этого использовать очередь, созданную с помощью [*менеджера*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-managers).403>404> 1. После помещения объекта в пустую очередь может возникнуть бесконечно малая задержка, прежде чем метод [`empty()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.empty) очереди вернёт [`False`](https://python-all.ru/3.3/library/constants.html#False), и [`get_nowait()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.get_nowait) сможет вернуть значение, не вызывая [`queue.Empty`](https://python-all.ru/3.3/library/queue.html#queue.Empty).405> 2. Если несколько процессов помещают объекты в очередь, возможно получение объектов на другом конце не по порядку. Однако объекты, помещённые в очередь одним и тем же процессом, всегда будут идти друг за другом в ожидаемом порядке.406407> **Предупреждение**408>409> Если процесс завершается с помощью [`Process.terminate()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/3.3/library/os.html#os.kill) в то время, когда он пытается использовать [`Queue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue), данные в очереди скорее всего будут повреждены. Из-за этого любой другой процесс может получить исключение при попытке использовать очередь впоследствии.410411> **Предупреждение**412>413> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не вызывал [`JoinableQueue.cancel_join_thread`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread)), то этот процесс не завершится, пока все буферизованные элементы не будут сброшены в канал.414>415> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.416>417> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [*Рекомендации по программированию*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-programming).418419Пример использования очередей для межпроцессного взаимодействия см. в разделе [*Примеры*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-examples).420421#### `multiprocessing.Pipe([duplex])`422423Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection), представляющих концы канала.424425Если *duplex* равен `True` (по умолчанию), то канал двунаправленный. Если *duplex* равен `False`, то канал однонаправленный: `conn1` может использоваться только для получения сообщений, а `conn2` – только для отправки сообщений.426427#### `class multiprocessing.Queue([maxsize])`428429Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.430431Обычные исключения [`queue.Empty`](https://python-all.ru/3.3/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.3/library/queue.html#queue.Full) из модуля [`queue`](https://python-all.ru/3.3/library/queue.html#module-queue) стандартной библиотеки возбуждаются при тайм-ауте.432433[`Queue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue) реализует все методы [`queue.Queue`](https://python-all.ru/3.3/library/queue.html#queue.Queue), за исключением [`task_done()`](https://python-all.ru/3.3/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.3/library/queue.html#queue.Queue.join).434435#### `qsize()`436437Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.438439Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/3.3/library/exceptions.html#NotImplementedError) на платформах Unix, таких как Mac OS X, где `sem_getvalue()` не реализован.440441#### `empty()`442443Возвращает `True`, если очередь пуста, и `False` в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.444445#### `full()`446447Возвращает `True`, если очередь полна, и `False` в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.448449#### `put(obj[, block[, timeout]])`450451Помещает obj в очередь. Если необязательный аргумент *block* равен `True` (по умолчанию), а *timeout* равен `None` (по умолчанию), то блокируется при необходимости до появления свободного места. Если *timeout* – положительное число, то блокируется не более *timeout* секунд и вызывает исключение [`queue.Full`](https://python-all.ru/3.3/library/queue.html#queue.Full), если в течение этого времени свободное место не появилось. В противном случае (*block* равен `False`), помещает элемент в очередь, если свободное место доступно немедленно, иначе вызывает исключение [`queue.Full`](https://python-all.ru/3.3/library/queue.html#queue.Full) (*timeout* в этом случае игнорируется).452453#### `put_nowait(obj)`454455Эквивалентно `put(obj, False)`.456457#### `get([block[, timeout]])`458459Remove and return an item from the queue. If optional args *block* is `True` (the default) and *timeout* is `None` (the default), block if necessary until an item is available. If *timeout* is a positive number, it blocks at most *timeout* seconds and raises the [`queue.Empty`](https://python-all.ru/3.3/library/queue.html#queue.Empty) exception if no item was available within that time. Otherwise (block is `False`), return an item if one is immediately available, else raise the [`queue.Empty`](https://python-all.ru/3.3/library/queue.html#queue.Empty) exception (*timeout* is ignored in that case).460461#### `get_nowait()`462463Эквивалентно `get(False)`.464465У [`multiprocessing.Queue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue) есть несколько дополнительных методов, отсутствующих в [`queue.Queue`](https://python-all.ru/3.3/library/queue.html#queue.Queue). В большинстве кода эти методы обычно не нужны:466467#### `close()`468469Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.470471#### `join_thread()`472473Присоединяет фоновый поток. Используется только после вызова [`close()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.close). Блокирует выполнение до завершения фонового потока, гарантируя, что все данные из буфера будут сброшены в канал.474475По умолчанию, если процесс не является создателем очереди, то при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы [`join_thread()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.join_thread) не делала ничего.476477#### `cancel_join_thread()`478479Предотвращает блокирование [`join_thread()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.join_thread). В частности, это не даёт фоновому потоку автоматически присоединиться при завершении процесса – см. [`join_thread()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.join_thread).480481Более удачным названием для этого метода могло бы быть `allow_exit_without_flush()`. Его использование, скорее всего, приведёт к потере поставленных в очередь данных, и вам почти наверняка не понадобится его вызывать. Он действительно нужен только в том случае, если текущему процессу необходимо немедленно завершиться, не дожидаясь сброса данных из очереди в нижележащий канал, и вам всё равно, что данные будут потеряны.482483#### `class multiprocessing.SimpleQueue`484485Упрощённый тип [`очереди`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue), очень близкий к блокируемому [`Pipe`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Pipe).486487#### `empty()`488489Возвращает `True`, если очередь пуста, и `False` в противном случае.490491#### `get()`492493Извлекает и возвращает элемент из очереди.494495#### `put(item)`496497Помещает *item* в очередь.498499#### `class multiprocessing.JoinableQueue([maxsize])`500501[`JoinableQueue`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue), подкласс [`очереди`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно содержит методы [`task_done()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue.join).502503#### `task_done()`504505Указывает, что ранее поставленная задача выполнена. Используется потребителями очереди. Для каждого вызова [`get()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.get), которым была получена задача, последующий вызов [`task_done()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) сообщает очереди, что обработка задачи завершена.506507Если вызов [`join()`](https://python-all.ru/3.3/library/queue.html#queue.Queue.join) в данный момент блокирован, он возобновится, когда все элементы будут обработаны (то есть для каждого элемента, который был [`put()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.put) в очередь, был получен вызов [`task_done()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done)).508509Вызывает [`ValueError`](https://python-all.ru/3.3/library/exceptions.html#ValueError), если вызывается больше раз, чем было помещено элементов в очередь.510511#### `join()`512513Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.514515Счётчик незавершённых задач увеличивается каждый раз, когда элемент добавляется в очередь. Счётчик уменьшается, когда потребитель вызывает [`task_done()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы сообщить, что элемент получен и вся работа с ним завершена. Когда счётчик незавершённых задач падает до нуля, [`join()`](https://python-all.ru/3.3/library/queue.html#queue.Queue.join) разблокируется.516517### 17.2.2.3. Разное518519#### `multiprocessing.active_children()`520521Возвращает список всех активных дочерних процессов текущего процесса.522523Вызов этой функции приводит к «присоединению» любых процессов, которые уже завершились.524525#### `multiprocessing.cpu_count()`526527Возвращает количество процессоров в системе. Может вызвать [`NotImplementedError`](https://python-all.ru/3.3/library/exceptions.html#NotImplementedError).528529#### `multiprocessing.current_process()`530531Возвращает объект [`процесса`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.532533Аналог [`threading.current_thread()`](https://python-all.ru/3.3/library/threading.html#threading.current_thread).534535#### `multiprocessing.freeze_support()`536537Добавлена поддержка для случая, когда программа, использующая [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing), была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)538539Эту функцию нужно вызывать сразу после строки `if __name__ == '__main__'` главного модуля. Например:540541```python542from multiprocessing import Process, freeze_support543544def f():545 print('hello world!')546547if __name__ == '__main__':548 freeze_support()549 Process(target=f).start()550```551552Если строку `freeze_support()` опустить, то при попытке запустить замороженный исполняемый файл будет вызвано [`RuntimeError`](https://python-all.ru/3.3/library/exceptions.html#RuntimeError).553554Если модуль запускается обычным образом интерпретатором Python, то [`freeze_support()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.freeze_support) не действует.555556#### `multiprocessing.set_executable()`557558Задаёт путь к интерпретатору Python, который будет использоваться при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/3.3/library/sys.html#sys.executable)). Встраивающим Python разработчикам, вероятно, потребуется сделать что-то вроде559560```python561set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))562```563564прежде чем они смогут создавать дочерние процессы. (Только для Windows)565566> **Примечание**567>568> [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) не содержит аналогов [`threading.active_count()`](https://python-all.ru/3.3/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/3.3/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/3.3/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/3.3/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/3.3/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/3.3/library/threading.html#threading.local).569570### 17.2.2.4. Объекты соединений571572Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.573574Объекты соединения обычно создаются с помощью [`Pipe()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Pipe) – см. также [*Слушатели и клиенты*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-listeners-clients).575576#### `class multiprocessing.Connection`577578#### `send(obj)`579580Отправляет объект на другой конец соединения; этот объект должен быть прочитан с помощью [`recv()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.recv).581582Объект должен быть сериализуем (picklable). Очень большие объекты (примерно от 32 МБ, хотя это зависит от ОС) могут вызвать исключение ValueError.583584#### `recv()`585586Возвращает объект, отправленный с другого конца соединения с помощью [`send()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.send). Блокируется, пока не появится что-то для приёма. Возбуждает [`EOFError`](https://python-all.ru/3.3/library/exceptions.html#EOFError), если больше нечего принимать и другой конец был закрыт.587588#### `fileno()`589590Возвращает файловый дескриптор или дескриптор (handle), используемый подключением.591592#### `close()`593594Закрывает подключение.595596Вызывается автоматически при сборке мусора для подключения.597598#### `poll([timeout])`599600Возвращает, есть ли данные для чтения.601602Если *timeout* не указан, то возврат происходит немедленно. Если *timeout* – число, оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, используется бесконечное ожидание.603604Обратите внимание, что несколько объектов соединения можно опрашивать одновременно с помощью [`multiprocessing.connection.wait()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.connection.wait).605606#### `send_bytes(buffer[, offset[, size]])`607608Отправляет байтовые данные из [*байтоподобного объекта*](https://python-all.ru/3.3/glossary.html#term-bytes-like-object) как полное сообщение.609610Если задан *offset*, то данные читаются с этой позиции в *buffer*. Если задан *size*, то из буфера будет прочитано соответствующее количество байт. Очень большие буферы (примерно от 32 МБ, хотя зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/3.3/library/exceptions.html#ValueError).611612#### `recv_bytes([maxlength])`613614Возвращает в виде строки полное байтовое сообщение, отправленное с другого конца соединения. Блокируется до появления данных для приёма. Вызывает [`EOFError`](https://python-all.ru/3.3/library/exceptions.html#EOFError), если больше нечего получать и другой конец закрыт.615616Если указан *maxlength* и сообщение длиннее *maxlength*, то вызывается [`OSError`](https://python-all.ru/3.3/library/exceptions.html#OSError), и соединение больше нельзя будет читать.617618Изменено в версии 3.3: Раньше эта функция вызывала [`IOError`](https://python-all.ru/3.3/library/exceptions.html#IOError), который теперь является псевдонимом [`OSError`](https://python-all.ru/3.3/library/exceptions.html#OSError).619620#### `recv_bytes_into(buffer[, offset])`621622Читает в *buffer* полное байтовое сообщение, отправленное с другого конца соединения, и возвращает количество байт в сообщении. Блокируется до появления данных для приёма. Вызывает [`EOFError`](https://python-all.ru/3.3/library/exceptions.html#EOFError), если больше нечего получать и другой конец закрыт.623624*buffer* должен быть доступным для записи [*байтоподобным объектом*](https://python-all.ru/3.3/glossary.html#term-bytes-like-object). Если задан *offset*, то сообщение будет записано в буфер, начиная с этой позиции. Offset должен быть неотрицательным целым числом, меньшим длины *buffer* (в байтах).625626Если буфер слишком мал, то вызывается исключение [`BufferTooShort`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.BufferTooShort), и полное сообщение доступно как `e.args[0]`, где `e` – это экземпляр исключения.627628Изменено в версии 3.3: Теперь сами объекты Connection можно передавать между процессами с помощью [`Connection.send()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.send) и [`Connection.recv()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.recv).629630Новое в версии 3.3: Объекты Connection теперь поддерживают протокол менеджера контекста – см. [*Типы менеджеров контекста*](https://python-all.ru/3.3/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__enter__) возвращает объект соединения, а [`__exit__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__exit__) вызывает [`close()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.close).631632Например:633634```python635>>> from multiprocessing import Pipe636>>> a, b = Pipe()637>>> a.send([1, 'hello', None])638>>> b.recv()639[1, 'hello', None]640>>> b.send_bytes(b'thank you')641>>> a.recv_bytes()642b'thank you'643>>> import array644>>> arr1 = array.array('i', range(5))645>>> arr2 = array.array('i', [0] * 10)646>>> a.send_bytes(arr1)647>>> count = b.recv_bytes_into(arr2)648>>> assert count == len(arr1) * arr1.itemsize649>>> arr2650array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])651```652653> **Предупреждение**654>655> Метод [`Connection.recv()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.recv) автоматически десериализует полученные данные, что может представлять угрозу безопасности, если нет доверия к процессу, отправившему сообщение.656>657> Поэтому, если объект соединения не был создан с помощью [`Pipe()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Pipe), методы [`recv()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.recv) и [`send()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.send) следует использовать только после выполнения некоторой аутентификации. См. [*Authentication keys*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-auth-keys).658659> **Предупреждение**660>661> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.662663### 17.2.2.5. Примитивы синхронизации664665Обычно примитивы синхронизации не так необходимы в многопроцессной программе, как в многопоточной. См. документацию модуля [`threading`](https://python-all.ru/3.3/library/threading.html#module-threading).666667Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [*Менеджеры*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-managers).668669#### `class multiprocessing.Barrier(parties[, action[, timeout]])`670671Объект барьера: клон [`threading.Barrier`](https://python-all.ru/3.3/library/threading.html#threading.Barrier).672673Новое в версии 3.3.674675#### `class multiprocessing.BoundedSemaphore([value])`676677Объект ограниченного семафора: копия [`threading.BoundedSemaphore`](https://python-all.ru/3.3/library/threading.html#threading.BoundedSemaphore).678679(На Mac OS X этот объект неотличим от [`Semaphore`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализована на этой платформе).680681#### `class multiprocessing.Condition([lock])`682683Условная переменная: псевдоним для [`threading.Condition`](https://python-all.ru/3.3/library/threading.html#threading.Condition).684685Если указана *блокировка*, то это должен быть объект [`Lock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.RLock) из [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing).686687Изменено в версии 3.3: Был добавлен метод [`wait_for()`](https://python-all.ru/3.3/library/threading.html#threading.Condition.wait_for).688689#### `class multiprocessing.Event`690691Клон [`threading.Event`](https://python-all.ru/3.3/library/threading.html#threading.Event).692693#### `class multiprocessing.Lock`694695Объект нерекурсивной блокировки: копия [`threading.Lock`](https://python-all.ru/3.3/library/threading.html#threading.Lock).696697#### `class multiprocessing.RLock`698699Объект рекурсивной блокировки: копия [`threading.RLock`](https://python-all.ru/3.3/library/threading.html#threading.RLock).700701#### `class multiprocessing.Semaphore([value])`702703Объект семафора: копия [`threading.Semaphore`](https://python-all.ru/3.3/library/threading.html#threading.Semaphore).704705> **Примечание**706>707> Методы `acquire()` и `wait()` для каждого из этих типов обрабатывают отрицательные таймауты как нулевые. Это отличается от [`threading`](https://python-all.ru/3.3/library/threading.html#module-threading), где начиная с версии 3.2 соответствующие методы `acquire()` обрабатывают отрицательные таймауты как бесконечные.708>709> В Mac OS X функция `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с таймаутом будет эмулировать её поведение с помощью цикла ожидания.710711> **Примечание**712>713> Если сигнал SIGINT, сгенерированный Ctrl-C, поступает в то время, как главный поток заблокирован вызовом `BoundedSemaphore.acquire()`, `Lock.acquire()`, `RLock.acquire()`, `Semaphore.acquire()`, `Condition.acquire()` или `Condition.wait()`, то вызов будет немедленно прерван и будет возбуждено [`KeyboardInterrupt`](https://python-all.ru/3.3/library/exceptions.html#KeyboardInterrupt).714>715> Это отличается от поведения [`threading`](https://python-all.ru/3.3/library/threading.html#module-threading), где SIGINT будет игнорироваться, пока выполняются эквивалентные блокирующие вызовы.716717### 17.2.2.6. Разделяемые [`ctypes`](https://python-all.ru/3.3/library/ctypes.html#module-ctypes) объекты718719Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.720721#### `multiprocessing.Value(typecode_or_type, *args, lock=True)`722723Возвращает объект [`ctypes`](https://python-all.ru/3.3/library/ctypes.html#module-ctypes), выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для объекта. Доступ к самому объекту можно получить через атрибут *value* объекта [`Value`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Value).724725*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.3/library/array.html#module-array). *\*args* передаётся конструктору типа.726727Если *lock* равен `True` (по умолчанию), то создаётся новый объект рекурсивной блокировки для синхронизации доступа к значению. Если *lock* является объектом [`Lock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *lock* равен `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».728729Операции, подобные `+=`, которые включают чтение и запись, не являются атомарными. Поэтому, если, например, требуется атомарно увеличить разделяемое значение, недостаточно просто сделать730731```python732counter.value += 1733```734735При условии, что связанная блокировка рекурсивна (по умолчанию она такой и является), можно вместо этого сделать736737```python738with counter.get_lock():739 counter.value += 1740```741742Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.743744#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`745746Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.747748*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.3/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* является последовательностью, которая используется для инициализации массива, и её длина определяет длину массива.749750Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».751752Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.753754Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.3/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.755756#### 17.2.2.6.1. Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing.sharedctypes)757758Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing.sharedctypes) предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/3.3/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.759760> **Примечание**761>762> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.763764#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`765766Возвращает массив ctypes, выделенный из разделяемой памяти.767768*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.3/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно задаёт длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.769770Обратите внимание, что запись и чтение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.771772#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`773774Возвращает объект ctypes, выделенный из разделяемой памяти.775776*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.3/library/array.html#module-array). *\*args* передаётся конструктору типа.777778Обратите внимание, что запись и чтение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.779780Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.3/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют хранить и извлекать строки – см. документацию по [`ctypes`](https://python-all.ru/3.3/library/ctypes.html#module-ctypes).781782#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *, lock=True)`783784То же, что и [`RawArray()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного массива ctypes.785786Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».787788Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.789790#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args, lock=True)`791792То же, что и [`RawValue()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного объекта ctypes.793794Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».795796Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.797798#### `multiprocessing.sharedctypes.copy(obj)`799800Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes *obj*.801802#### `multiprocessing.sharedctypes.synchronized(obj[, lock])`803804Возвращает процесс-безопасный объект-обёртку для объекта ctypes, который использует *блокировка* для синхронизации доступа. Если *блокировка* равно `None` (по умолчанию), то автоматически создаётся объект [`multiprocessing.RLock`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.RLock).805806Синхронизированная обёртка будет иметь два дополнительных метода по сравнению с объектом, который она оборачивает: `get_obj()` возвращает обёрнутый объект, а `get_lock()` возвращает объект блокировки, используемый для синхронизации.807808Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.809810В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некий подкласс [`ctypes.Structure`](https://python-all.ru/3.3/library/ctypes.html#ctypes.Structure).)811812| ctypes | sharedctypes с типом | sharedctypes с typecode |813| --- | --- | --- |814| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |815| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) | |816| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |817| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |818819Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:820821```python822from multiprocessing import Process, Lock823from multiprocessing.sharedctypes import Value, Array824from ctypes import Structure, c_double825826class Point(Structure):827 _fields_ = [('x', c_double), ('y', c_double)]828829def modify(n, x, s, A):830 n.value **= 2831 x.value **= 2832 s.value = s.value.upper()833 for a in A:834 a.x **= 2835 a.y **= 2836837if __name__ == '__main__':838 lock = Lock()839840 n = Value('i', 7)841 x = Value(c_double, 1.0/3.0, lock=False)842 s = Array('c', b'hello world', lock=lock)843 A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)844845 p = Process(target=modify, args=(n, x, s, A))846 p.start()847 p.join()848849 print(n.value)850 print(x.value)851 print(s.value)852 print([(a.x, a.y) for a in A])853```854855Выводятся следующие результаты:856857```text858498590.1111111111111111860HELLO WORLD861[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]862```863864### 17.2.2.7. Менеджеры865866Менеджеры предоставляют способ создания данных, которыми можно обмениваться между разными процессами, в том числе по сети между процессами, выполняющимися на разных машинах. Объект менеджера управляет серверным процессом, который управляет *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.867868#### `multiprocessing.Manager()`869870Возвращает запущенный объект [`SyncManager`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.SyncManager), который можно использовать для обмена объектами между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.871872Процессы менеджеров завершаются, как только они собираются сборщиком мусора или их родительский процесс завершается. Классы менеджеров определены в модуле [`multiprocessing.managers`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing.managers):873874#### `class multiprocessing.managers.BaseManager([address[, authkey]])`875876Создает объект BaseManager.877878После создания следует вызвать [`start()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или `get_server().serve_forever()`, чтобы гарантировать, что объект менеджера ссылается на запущенный процесс менеджера.879880*address* – это адрес, по которому процесс менеджера ожидает новые подключения. Если *address* равен `None`, то выбирается произвольный.881882*authkey* – это ключ аутентификации, который будет использоваться для проверки подлинности входящих подключений к процессу сервера. Если *authkey* равен `None`, то используется `current_process().authkey`. В противном случае используется *authkey*, и он должен быть строкой байтов.883884#### `start([initializer[, initargs]])`885886Запускает подпроцесс для запуска менеджера. Если *initializer* не равен `None`, то подпроцесс вызовет `initializer(*initargs)` при запуске.887888#### `get_server()`889890Возвращает объект `Server`, представляющий реальный сервер под управлением Manager. Объект `Server` поддерживает метод `serve_forever()`:891892```python893>>> from multiprocessing.managers import BaseManager894>>> manager = BaseManager(address=('', 50000), authkey=b'abc')895>>> server = manager.get_server()896>>> server.serve_forever()897```898899`Server` также имеет атрибут [`address`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).900901#### `connect()`902903Подключает локальный объект менеджера к удаленному процессу менеджера:904905```python906>>> from multiprocessing.managers import BaseManager907>>> m = BaseManager(address=('127.0.0.1', 5000), authkey=b'abc')908>>> m.connect()909```910911#### `shutdown()`912913Останавливает процесс, используемый менеджером. Это доступно только в том случае, если [`start()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.914915Этот метод можно вызывать несколько раз.916917#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`918919Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.920921*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.922923*callable* – это вызываемый объект, используемый для создания объектов для данного идентификатора типа. Если экземпляр менеджера будет подключен к серверу с помощью метода [`connect()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager.connect) или если аргумент *create\_method* равен `False`, то этот параметр можно оставить равным `None`.924925*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.926927*exposed* используется для указания последовательности имен методов, к которым прокси для данного typeid должны иметь доступ с помощью [`BaseProxy._callmethod()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). (Если *exposed* равен `None`, то вместо него используется `proxytype._exposed_`, если он существует.) В случае, когда список exposed не указан, все «публичные методы» разделяемого объекта будут доступны. (Здесь «публичный метод» означает любой атрибут, у которого есть метод [`__call__()`](https://python-all.ru/3.3/reference/datamodel.html#object.__call__) и имя которого не начинается с `'_'`.)928929*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно сопоставляет имена методов со строками typeid. (Если *method\_to\_typeid* равен `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом в этом отображении или если отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.930931*create\_method* определяет, должен ли быть создан метод с именем *typeid*, который можно использовать, чтобы сообщить серверному процессу о создании нового разделяемого объекта и возврате прокси для него. По умолчанию это `True`.932933Экземпляры [`BaseManager`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:934935#### `address`936937Адрес, используемый менеджером.938939Изменено в версии 3.3: Объекты Manager поддерживают протокол менеджера контекста – см. [*Context Manager Types*](https://python-all.ru/3.3/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__enter__) запускает серверный процесс (если он еще не запущен) и затем возвращает объект менеджера. [`__exit__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__exit__) вызывает [`shutdown()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager.shutdown).940941В предыдущих версиях [`__enter__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__enter__) не запускал серверный процесс менеджера, если он ещё не был запущен.942943#### `class multiprocessing.managers.SyncManager`944945Подкласс [`BaseManager`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.946947Он также поддерживает создание разделяемых списков и словарей.948949#### `Barrier(parties[, action[, timeout]])`950951Создаёт разделяемый объект [`threading.Barrier`](https://python-all.ru/3.3/library/threading.html#threading.Barrier) и возвращает прокси для него.952953Новое в версии 3.3.954955#### `BoundedSemaphore([value])`956957Создаёт разделяемый объект [`threading.BoundedSemaphore`](https://python-all.ru/3.3/library/threading.html#threading.BoundedSemaphore) и возвращает прокси для него.958959#### `Condition([lock])`960961Создаёт разделяемый объект [`threading.Condition`](https://python-all.ru/3.3/library/threading.html#threading.Condition) и возвращает прокси для него.962963Если *блокировка* указан, то он должен быть прокси для объекта [`threading.Lock`](https://python-all.ru/3.3/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/3.3/library/threading.html#threading.RLock).964965Изменено в версии 3.3: Был добавлен метод [`wait_for()`](https://python-all.ru/3.3/library/threading.html#threading.Condition.wait_for).966967#### `Event()`968969Создаёт разделяемый объект [`threading.Event`](https://python-all.ru/3.3/library/threading.html#threading.Event) и возвращает для него прокси.970971#### `Lock()`972973Создаёт разделяемый объект [`threading.Lock`](https://python-all.ru/3.3/library/threading.html#threading.Lock) и возвращает для него прокси.974975#### `Namespace()`976977Создаёт разделяемый объект [`Namespace`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.SyncManager.Namespace) и возвращает для него прокси.978979#### `Queue([maxsize])`980981Создаёт разделяемый объект [`queue.Queue`](https://python-all.ru/3.3/library/queue.html#queue.Queue) и возвращает для него прокси.982983#### `RLock()`984985Создаёт разделяемый объект [`threading.RLock`](https://python-all.ru/3.3/library/threading.html#threading.RLock) и возвращает для него прокси.986987#### `Semaphore([value])`988989Создаёт разделяемый объект [`threading.Semaphore`](https://python-all.ru/3.3/library/threading.html#threading.Semaphore) и возвращает прокси для него.990991#### `Array(typecode, sequence)`992993Создать массив и вернуть для него прокси.994995#### `Value(typecode, value)`996997Создаёт объект с атрибутом `value`, доступным для записи, и возвращает прокси для него.998999#### `dict()`10001001#### `dict(mapping)`10021003#### `dict(sequence)`10041005Создаёт разделяемый объект `dict` и возвращает для него прокси.10061007#### `list()`10081009#### `list(sequence)`10101011Создаёт разделяемый объект `list` и возвращает для него прокси.10121013> **Примечание**1014>1015> Изменения изменяемых значений или элементов в прокси-объектах словарей и списков не будут распространяться через менеджер, поскольку прокси не может отследить, когда его значения или элементы изменяются. Чтобы изменить такой элемент, можно переназначить изменённый объект обратно в прокси-контейнер:1016>1017> ```python1018> # создать прокси для списка и добавить изменяемый объект (словарь)1019> lproxy = manager.list()1020> lproxy.append({})1021> # теперь изменить словарь1022> d = lproxy[0]1023> d['a'] = 11024> d['b'] = 21025> # на данный момент изменения в d еще не синхронизированы, но при1026> # при переприсваивании словаря прокси получает уведомление об изменении1027> lproxy[0] = d1028> ```10291030#### 17.2.2.7.1. Namespace-объекты10311032Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.10331034Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:10351036```python1037>>> manager = multiprocessing.Manager()1038>>> Global = manager.Namespace()1039>>> Global.x = 101040>>> Global.y = 'hello'1041>>> Global._z = 12.3 # это атрибут прокси1042>>> print(Global)1043Namespace(x=10, y='hello')1044```10451046#### 17.2.2.7.2. Настраиваемые менеджеры10471048Чтобы создать собственный менеджер, создаётся подкласс [`BaseManager`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager) и используется метод класса [`register()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:10491050```python1051from multiprocessing.managers import BaseManager10521053class MathsClass:1054 def add(self, x, y):1055 return x + y1056 def mul(self, x, y):1057 return x * y10581059class MyManager(BaseManager):1060 pass10611062MyManager.register('Maths', MathsClass)10631064if __name__ == '__main__':1065 with MyManager() as manager:1066 maths = manager.Maths()1067 print(maths.add(4, 3)) # выводит 71068 print(maths.mul(7, 8)) # выводит 561069```10701071#### 17.2.2.7.3. Использование удалённого менеджера10721073Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).10741075Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:10761077```python1078>>> from multiprocessing.managers import BaseManager1079>>> import queue1080>>> queue = queue.Queue()1081>>> class QueueManager(BaseManager): pass1082>>> QueueManager.register('get_queue', callable=lambda:queue)1083>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1084>>> s = m.get_server()1085>>> s.serve_forever()1086```10871088Один клиент может получить доступ к серверу следующим образом:10891090```python1091>>> from multiprocessing.managers import BaseManager1092>>> class QueueManager(BaseManager): pass1093>>> QueueManager.register('get_queue')1094>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1095>>> m.connect()1096>>> queue = m.get_queue()1097>>> queue.put('hello')1098```10991100Другой клиент также может использовать его:11011102```python1103>>> from multiprocessing.managers import BaseManager1104>>> class QueueManager(BaseManager): pass1105>>> QueueManager.register('get_queue')1106>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1107>>> m.connect()1108>>> queue = m.get_queue()1109>>> queue.get()1110'hello'1111```11121113Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:11141115```python1116>>> from multiprocessing import Process, Queue1117>>> from multiprocessing.managers import BaseManager1118>>> class Worker(Process):1119... def __init__(self, q):1120... self.q = q1121... super(Worker, self).__init__()1122... def run(self):1123... self.q.put('local hello')1124...1125>>> queue = Queue()1126>>> w = Worker(queue)1127>>> w.start()1128>>> class QueueManager(BaseManager): pass1129...1130>>> QueueManager.register('get_queue', callable=lambda: queue)1131>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1132>>> s = m.get_server()1133>>> s.serve_forever()1134```11351136### 17.2.2.8. Прокси-объекты11371138Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.11391140Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:11411142```python1143>>> from multiprocessing import Manager1144>>> manager = Manager()1145>>> l = manager.list([i*i for i in range(10)])1146>>> print(l)1147[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1148>>> print(repr(l))1149<ListProxy object, typeid 'list' at 0x...>1150>>> l[4]1151161152>>> l[2:5]1153[4, 9, 16]1154```11551156Обратите внимание, что применение [`str()`](https://python-all.ru/3.3/library/stdtypes.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/3.3/library/functions.html#repr) вернёт представление прокси.11571158Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:11591160```python1161>>> a = manager.list()1162>>> b = manager.list()1163>>> a.append(b) # референт a теперь содержит референт b1164>>> print(a, b)1165[[]] []1166>>> b.append('hello')1167>>> print(a, b)1168[['hello']] ['hello']1169```11701171> **Примечание**1172>1173> Типы прокси в [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) не поддерживают сравнение по значению. Так, например, имеем:1174>1175> ```python1176> >>> manager.list([1,2,3]) == [1,2,3]1177> False1178> ```1179>1180> При сравнении следует просто использовать копию референта.11811182#### `class multiprocessing.managers.BaseProxy`11831184Объекты прокси являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseProxy).11851186#### `_callmethod(methodname[, args[, kwds]])`11871188Вызывает метод референта прокси и возвращает результат.11891190Если `proxy` является прокси, чей референт – `obj`, то выражение11911192```python1193proxy._callmethod(methodname, args, kwds)1194```11951196вычислит выражение11971198```python1199getattr(obj, methodname)(*args, **kwds)1200```12011202в процессе менеджера.12031204Возвращаемое значение будет копией результата вызова или прокси на новый разделяемый объект – см. документацию по аргументу *method\_to\_typeid* метода [`BaseManager.register()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).12051206Если вызов порождает исключение, оно возбуждается повторно в [`_callmethod()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). Если в процессе менеджера возникло другое исключение, оно преобразуется в исключение `RemoteError` и возбуждается в [`_callmethod()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod).12071208Обратите внимание: если *methodname* не был *опубликован*, будет возбуждено исключение.12091210Пример использования [`_callmethod()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):12111212```python1213>>> l = manager.list(range(10))1214>>> l._callmethod('__len__')1215101216>>> l._callmethod('__getslice__', (2, 7)) # эквивалентно `l[2:7]`1217[2, 3, 4, 5, 6]1218>>> l._callmethod('__getitem__', (20,)) # эквивалентно `l[20]`1219Traceback (most recent call last):1220...1221IndexError: list index out of range1222```12231224#### `_getvalue()`12251226Возвращает копию референта.12271228Если референт не может быть сериализован (unpicklable), то это вызовет исключение.12291230#### `__repr__()`12311232Возвращает представление прокси-объекта.12331234#### `__str__()`12351236Возвращает представление референта.12371238#### 17.2.2.8.1. Очистка12391240Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.12411242Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.12431244### 17.2.2.9. Пулы процессов12451246Можно создать пул процессов, которые будут выполнять переданные ему задачи с помощью класса [`Pool`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool).12471248#### `class multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild]]]])`12491250Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.12511252*processes* – количество используемых рабочих процессов. Если *processes* равно `None`, используется число, возвращаемое `cpu_count()`. Если *initializer* не `None`, каждый рабочий процесс при запуске вызовет `initializer(*initargs)`.12531254Обратите внимание, что методы объекта пула должны вызываться только тем процессом, который создал пул.12551256Новое в версии 3.2: *maxtasksperchild* – количество задач, которые рабочий процесс может выполнить перед завершением и заменой новым процессом, чтобы освободить неиспользуемые ресурсы. По умолчанию *maxtasksperchild* равен None, то есть рабочие процессы живут столько же, сколько и пул.12571258> **Примечание**1259>1260> Рабочие процессы внутри [`Pool`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool) обычно живут всё время существования очереди задач пула. Частая практика в других системах (например, Apache, mod\_wsgi и т.д.) для освобождения ресурсов, занятых рабочими процессами, – разрешить процессу выполнить только определённое количество работы, после чего он завершается, очищается и заменяется новым процессом. Аргумент *maxtasksperchild* конструктора [`Pool`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool) предоставляет эту возможность конечному пользователю.12611262#### `apply(func[, args[, kwds]])`12631264Вызывает *func* с аргументами *args* и именованными аргументами *kwds*. Блокирует выполнение до получения результата. Поскольку этот метод блокирует, [`apply_async()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.apply_async) лучше подходит для параллельной работы. Кроме того, *func* выполняется только в одном из рабочих процессов пула.12651266#### `apply_async(func[, args[, kwds[, callback[, error_callback]]]])`12671268Вариант метода [`apply()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.apply), который возвращает объект результата.12691270Если *колбэк* указан, он должен быть вызываемым объектом, принимающим один аргумент. Когда результат готов, вызывается *колбэк*, если только вызов не завершился ошибкой; в этом случае вместо него вызывается *error\_callback*.12711272Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.12731274Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.12751276#### `map(func, iterable[, chunksize])`12771278Параллельный аналог встроенной функции [`map()`](https://python-all.ru/3.3/library/functions.html#map) (однако поддерживает только один аргумент *iterable*). Блокирует выполнение до получения результата.12791280Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.12811282#### `map_async(func, iterable[, chunksize[, callback[, error_callback]]])`12831284Вариант метода [`map()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.map), который возвращает объект результата.12851286Если *колбэк* указан, он должен быть вызываемым объектом, принимающим один аргумент. Когда результат готов, вызывается *колбэк*, если только вызов не завершился ошибкой; в этом случае вместо него вызывается *error\_callback*.12871288Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.12891290Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.12911292#### `imap(func, iterable[, chunksize])`12931294Более ленивая версия [`map()`](https://python-all.ru/3.3/library/functions.html#map).12951296The *chunksize* argument is the same as the one used by the [`map()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.map) method. For very long iterables using a large value for *chunksize* can make the job complete **much** faster than using the default value of `1`.12971298Кроме того, если *chunksize* равен `1`, то метод `next()` итератора, возвращаемого методом [`imap()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` возбудит [`multiprocessing.TimeoutError`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.TimeoutError), если результат не будет получен в течение *timeout* секунд.12991300#### `imap_unordered(func, iterable[, chunksize])`13011302То же, что и [`imap()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.imap), но порядок результатов из возвращаемого итератора может быть произвольным. (Только при наличии одного рабочего процесса порядок гарантированно «правильный».)13031304#### `starmap(func, iterable[, chunksize])`13051306Как [`map()`](https://python-all.ru/3.3/library/functions.html#map), но элементы *iterable* должны быть итерируемыми объектами, которые распаковываются как аргументы.13071308Следовательно, *iterable* из *\[(1,2), (3, 4)\]* даёт результат *\[func(1,2), func(3,4)\]*.13091310Новое в версии 3.3.13111312#### `starmap_async(func, iterable[, chunksize[, callback[, error_back]]])`13131314Комбинация [`starmap()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.starmap) и [`map_async()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.map_async), которая перебирает *iterable* – итерируемый объект, содержащий итерируемые объекты – и вызывает *func* с распакованными итерируемыми объектами. Возвращает объект-результат.13151316Новое в версии 3.3.13171318#### `close()`13191320Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.13211322#### `terminate()`13231324Немедленно останавливает рабочие процессы, не завершая незавершённую работу. Когда объект пула собирается сборщиком мусора, [`terminate()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.terminate) будет вызван немедленно.13251326#### `join()`13271328Ожидает завершения рабочих процессов. Перед вызовом [`join()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.join) необходимо вызвать [`close()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.close) или [`terminate()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.terminate).13291330Новое в версии 3.3: Объекты Pool теперь поддерживают протокол менеджера контекста – см. [*Типы менеджеров контекста*](https://python-all.ru/3.3/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__enter__) возвращает объект пула, а [`__exit__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__exit__) вызывает [`terminate()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.terminate).13311332#### `class multiprocessing.pool.AsyncResult`13331334Класс результата, возвращаемого [`Pool.apply_async()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.apply_async) и [`Pool.map_async()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool.map_async).13351336#### `get([timeout])`13371338Возвращает результат по его получении. Если *timeout* не равен `None` и результат не получен в течение *timeout* секунд, то возбуждается [`multiprocessing.TimeoutError`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.TimeoutError). Если удалённый вызов породил исключение, то оно будет возбуждено повторно вызовом [`get()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get).13391340#### `wait([timeout])`13411342Ожидает, пока результат не станет доступен или пока не пройдёт *timeout* секунд.13431344#### `ready()`13451346Возвращает, завершён ли вызов.13471348#### `successful()`13491350Возвращает, завершился ли вызов без возбуждения исключения. Возбуждает [`AssertionError`](https://python-all.ru/3.3/library/exceptions.html#AssertionError), если результат ещё не готов.13511352Следующий пример демонстрирует использование пула:13531354```python1355from multiprocessing import Pool13561357def f(x):1358 return x*x13591360if __name__ == '__main__':1361 with Pool(processes=4) as pool: # запустить 4 рабочих процесса1362 result = pool.apply_async(f, (10,)) # асинхронно вычислить "f(10)"1363 print(result.get(timeout=1)) # выводит "100", если компьютер не *очень* медленный13641365 print(pool.map(f, range(10))) # выводит "[0, 1, 4,..., 81]"13661367 it = pool.imap(f, range(10))1368 print(next(it)) # выводит "0"1369 print(next(it)) # выводит "1"1370 print(it.next(timeout=1)) # выводит "4", если компьютер не *очень* медленный13711372 import time1373 result = pool.apply_async(time.sleep, (10,))1374 print(result.get(timeout=1)) # вызывает TimeoutError1375```13761377### 17.2.2.10. Слушатели и клиенты13781379Обычно передача сообщений между процессами осуществляется через очереди или с помощью объектов [`Connection`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection), возвращаемых [`Pipe()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Pipe).13801381Однако модуль [`multiprocessing.connection`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing.connection) обеспечивает дополнительную гибкость. По сути, он предоставляет высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows. Он также поддерживает *дайджест-аутентификацию* с помощью модуля [`hmac`](https://python-all.ru/3.3/library/hmac.html#module-hmac) и возможность опроса нескольких соединений одновременно.13821383#### `multiprocessing.connection.deliver_challenge(connection, authkey)`13841385Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.13861387Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.AuthenticationError).13881389#### `multiprocessing.connection.answer_challenge(connection, authkey)`13901391Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.13921393Если приветственное сообщение не получено, то возбуждается [`AuthenticationError`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.AuthenticationError).13941395#### `multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`13961397Пытается установить соединение с прослушивателем, который использует адрес *address*, и возвращает объект [`Connection`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection).13981399Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [*Address Formats*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-address-formats))14001401Если *authenticate* равен `True` или *authkey* является байтовой строкой, то используется дайджест-аутентификация. Ключом для аутентификации будет либо *authkey*, либо `current_process().authkey`, если *authkey* равен `None`. Если аутентификация не удалась, то возбуждается [`AuthenticationError`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.AuthenticationError). См. [*Authentication keys*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-auth-keys).14021403#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`14041405Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.14061407*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.14081409> **Примечание**1410>1411> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.14121413*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них только первый гарантированно доступен. Если *family* равен `None`, то семейство определяется по формату *address*. Если *address* также равен `None`, то выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [*Address Formats*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равен `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/3.3/library/tempfile.html#tempfile.mkstemp).14141415Если объект прослушивателя использует сокет, то *backlog* (по умолчанию 1) передаётся методу [`listen()`](https://python-all.ru/3.3/library/socket.html#socket.socket.listen) сокета после его привязки.14161417Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.14181419Если *authkey* является байтовой строкой, то она будет использоваться в качестве ключа аутентификации; в противном случае она должна быть *None*.14201421Если *authkey* равен `None`, а *authenticate* равен `True`, то в качестве ключа аутентификации используется `current_process().authkey`. Если *authkey* равен `None`, а *authenticate* равен `False`, то аутентификация не выполняется. Если аутентификация не удалась, то возбуждается [`AuthenticationError`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.AuthenticationError). См. [*Authentication keys*](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing-auth-keys).14221423#### `accept()`14241425Принимает соединение на привязанном сокете или именованном канале объекта прослушивателя и возвращает объект [`Connection`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection). Если предпринята попытка аутентификации и она не удалась, то возбуждается [`AuthenticationError`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.AuthenticationError).14261427#### `close()`14281429Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.14301431Объекты Listener имеют следующие свойства только для чтения:14321433#### `address`14341435Адрес, используемый объектом Listener.14361437#### `last_accepted`14381439Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то `None`.14401441Новое в версии 3.3: Объекты Listener теперь поддерживают протокол контекстного менеджера – см. [*Типы контекстных менеджеров*](https://python-all.ru/3.3/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__enter__) возвращает объект Listener, а [`__exit__()`](https://python-all.ru/3.3/library/stdtypes.html#contextmanager.__exit__) вызывает [`close()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.connection.Listener.close).14421443#### `multiprocessing.connection.wait(object_list, timeout=None)`14441445Ожидает, пока какой-либо объект из *object\_list* не станет готовым. Возвращает список тех объектов из *object\_list*, которые готовы. Если *timeout* – число с плавающей запятой, то вызов блокируется не более чем на указанное количество секунд. Если *timeout* равен `None`, то блокировка будет продолжаться неограниченно долго. Отрицательное значение timeout эквивалентно нулевому.14461447Как в Unix, так и в Windows объект может появиться в *object\_list*, если он14481449- читаемый объект [`Connection`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection);1450- подключённый и читаемый объект [`socket.socket`](https://python-all.ru/3.3/library/socket.html#socket.socket); или1451- атрибут [`sentinel`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.sentinel) объекта [`процесса`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process).14521453Объект соединения или сокета готов, когда из него доступны для чтения данные или другой конец был закрыт.14541455**Unix**: `wait(object_list, timeout)` почти эквивалентна `select.select(object_list, [], [], timeout)`. Разница в том, что если [`select.select()`](https://python-all.ru/3.3/library/select.html#select.select) прерывается сигналом, она может возбудить [`OSError`](https://python-all.ru/3.3/library/exceptions.html#OSError) с кодом ошибки `EINTR`, тогда как [`wait()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.connection.wait) этого не делает.14561457**Windows**: Элемент в *object\_list* должен быть либо целочисленным дескриптором, который может ожидаться (согласно определению, используемому в документации функции Win32 `WaitForMultipleObjects()`), либо объектом с методом `fileno()`, который возвращает дескриптор сокета или дескриптор канала. (Обратите внимание, что дескрипторы каналов и сокетов **не** являются ожидаемыми дескрипторами.)14581459Новое в версии 3.3.14601461**Примеры**14621463Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:14641465```python1466from multiprocessing.connection import Listener1467from array import array14681469address = ('localhost', 6000) # семейство определяется как 'AF_INET'14701471with Listener(address, authkey=b'secret password') as listener:1472 with listener.accept() as conn:1473 print('connection accepted from', listener.last_accepted)14741475 conn.send([2.25, None, 'junk', float])14761477 conn.send_bytes(b'hello')14781479 conn.send_bytes(array('i', [42, 1729]))1480```14811482Следующий код подключается к серверу и получает от него некоторые данные:14831484```python1485from multiprocessing.connection import Client1486from array import array14871488address = ('localhost', 6000)14891490with Client(address, authkey=b'secret password') as conn:1491 print(conn.recv()) # => [2.25, None, 'junk', float]14921493 print(conn.recv_bytes()) # => 'hello'14941495 arr = array('i', [0, 0, 0, 0, 0])1496 print(conn.recv_bytes_into(arr)) # => 81497 print(arr) # => array('i', [42, 1729, 0, 0, 0])1498```14991500Следующий код использует [`wait()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.connection.wait) для ожидания сообщений сразу от нескольких процессов:15011502```python1503import time, random1504from multiprocessing import Process, Pipe, current_process1505from multiprocessing.connection import wait15061507def foo(w):1508 for i in range(10):1509 w.send((i, current_process().name))1510 w.close()15111512if __name__ == '__main__':1513 readers = []15141515 for i in range(4):1516 r, w = Pipe(duplex=False)1517 readers.append(r)1518 p = Process(target=foo, args=(w,))1519 p.start()1520 # Закрываем записывающий конец канала, чтобы убедиться, что1521 # p – единственный процесс, владеющий дескриптором канала. Это1522 # гарантирует, что когда p закроет свой дескриптор для записывающего конца,1523 # wait() немедленно сообщит, что читающий конец готов1524 w.close()15251526 while readers:1527 for r in wait(readers):1528 try:1529 msg = r.recv()1530 except EOFError:1531 readers.remove(r)1532 else:1533 print(msg)1534```15351536#### 17.2.2.10.1. Форматы адресов15371538- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.1539- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.1540- **Адрес `'AF_PIPE'` – это строка вида**15411542 `r'\\.\pipe\PipeName'`. Чтобы использовать [`Client()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.connection.Client) для подключения к именованному каналу на удалённом компьютере с именем *ServerName*, следует использовать адрес вида `r'\\ServerName\pipe\PipeName'` вместо.15431544Обратите внимание, что любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не адресом `'AF_UNIX'`.15451546### 17.2.2.11. Ключи аутентификации15471548Когда используется [`Connection.recv`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Connection.recv), полученные данные автоматически десериализуются (unpickled). К сожалению, десериализация данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/3.3/library/hmac.html#module-hmac) для обеспечения дайджест-аутентификации.15491550Ключ аутентификации – это байтовая строка, которую можно рассматривать как пароль: после установления соединения обе стороны требуют доказательства, что другая сторона знает ключ аутентификации. (Демонстрация того, что обе стороны используют один и тот же ключ, **не** предполагает отправку ключа по соединению.)15511552Если запрошена аутентификация, но ключ аутентификации не указан, то используется возвращаемое значение `current_process().authkey` (см. [`процесс`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process)). Это значение будет автоматически унаследовано любым объектом [`процесса`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process), который создаёт текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы будут использовать единый ключ аутентификации, который можно применять при установке соединений между ними.15531554Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/3.3/library/os.html#os.urandom).15551556### 17.2.2.12. Логирование15571558Доступна некоторая поддержка логирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/3.3/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому возможно (в зависимости от типа обработчика) перемешивание сообщений от разных процессов.15591560#### `multiprocessing.get_logger()`15611562Возвращает регистратор (logger), используемый модулем [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing). При необходимости будет создан новый.15631564При первом создании регистратор имеет уровень `logging.NOTSET` и не имеет обработчика по умолчанию. Сообщения, отправленные этому регистратору, по умолчанию не распространяются на корневой регистратор.15651566Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.15671568#### `multiprocessing.log_to_stderr()`15691570Эта функция вызывает [`get_logger()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.get_logger), но в дополнение к возврату регистратора, созданного get\_logger, она добавляет обработчик, который отправляет вывод в [`sys.stderr`](https://python-all.ru/3.3/library/sys.html#sys.stderr), используя формат `'[%(levelname)s/%(processName)s] %(message)s'`.15711572Ниже приведён пример сеанса с включённым журналированием:15731574```python1575>>> import multiprocessing, logging1576>>> logger = multiprocessing.log_to_stderr()1577>>> logger.setLevel(logging.INFO)1578>>> logger.warning('doomed')1579[WARNING/MainProcess] doomed1580>>> m = multiprocessing.Manager()1581[INFO/SyncManager-...] child process calling self.run()1582[INFO/SyncManager-...] created temp directory /.../pymp-...1583[INFO/SyncManager-...] manager serving at '/.../listener-...'1584>>> del m1585[INFO/MainProcess] sending shutdown message to manager1586[INFO/SyncManager-...] manager exiting with exitcode 01587```15881589В дополнение к этим двум функциям ведения журнала, multiprocessing также предоставляет два дополнительных атрибута уровня журналирования. Это `SUBWARNING` и `SUBDEBUG`. Приведенная ниже таблица показывает, где они располагаются в обычной иерархии уровней.15901591| Уровень | Числовое значение |1592| --- | --- |1593| `SUBWARNING` | 25 |1594| `SUBDEBUG` | 5 |15951596Полную таблицу уровней логирования см. в модуле [`logging`](https://python-all.ru/3.3/library/logging.html#module-logging).15971598Эти дополнительные уровни журналирования используются в основном для некоторых отладочных сообщений в модуле multiprocessing. Ниже приведен тот же пример, что и выше, но с включенным `SUBDEBUG`:15991600```python1601>>> import multiprocessing, logging1602>>> logger = multiprocessing.log_to_stderr()1603>>> logger.setLevel(multiprocessing.SUBDEBUG)1604>>> logger.warning('doomed')1605[WARNING/MainProcess] doomed1606>>> m = multiprocessing.Manager()1607[INFO/SyncManager-...] child process calling self.run()1608[INFO/SyncManager-...] created temp directory /.../pymp-...1609[INFO/SyncManager-...] manager serving at '/.../pymp-djGBXN/listener-...'1610>>> del m1611[SUBDEBUG/MainProcess] finalizer calling ...1612[INFO/MainProcess] sending shutdown message to manager1613[DEBUG/SyncManager-...] manager received shutdown message1614[SUBDEBUG/SyncManager-...] calling <Finalize object, callback=unlink, ...1615[SUBDEBUG/SyncManager-...] finalizer calling <built-in function unlink> ...1616[SUBDEBUG/SyncManager-...] calling <Finalize object, dead>1617[SUBDEBUG/SyncManager-...] finalizer calling <function rmtree at 0x5aa730> ...1618[INFO/SyncManager-...] manager exiting with exitcode 01619```16201621### 17.2.2.13. Модуль [`multiprocessing.dummy`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing.dummy)16221623[`multiprocessing.dummy`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing.dummy) воспроизводит API модуля [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing), но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/3.3/library/threading.html#module-threading).16241625## 17.2.3. Рекомендации по программированию16261627Существуют определённые рекомендации и идиомы, которые следует соблюдать при использовании [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing).16281629### 17.2.3.1. Все платформы16301631Избегайте разделяемого состояния16321633> По возможности следует стараться избегать передачи больших объёмов данных между процессами.1634>1635> Вероятно, лучше всего ограничиться использованием очередей или каналов для связи между процессами, а не использовать низкоуровневые примитивы синхронизации.16361637Сериализуемость16381639> Убедитесь, что аргументы методов прокси-объектов сериализуемы.16401641Потокобезопасность прокси16421643> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.1644>1645> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)16461647Присоединение процессов-зомби16481649> На Unix, когда процесс завершается, но к нему ещё не присоединились, он становится зомби. Их никогда не должно быть много, потому что каждый раз, когда запускается новый процесс (или вызывается1650>1651> [`active_children()`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.active_children)1652>1653> ), все завершённые процессы, к которым ещё не присоединились, будут присоединены. Кроме того, вызов1654>1655> [`Process.is_alive`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.is_alive)1656>1657> для завершённого процесса также присоединяет его. Тем не менее, вероятно, хорошей практикой является явное присоединение всех запущенных процессов.16581659Лучше наследовать, чем упаковывать/распаковывать16601661> В Windows многие типы из1662>1663> [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing)1664>1665> должны быть сериализуемы (picklable), чтобы дочерние процессы могли их использовать. Однако в целом следует избегать передачи общих объектов другим процессам через каналы (pipes) или очереди. Вместо этого нужно организовать программу так, чтобы процесс, которому требуется доступ к общему ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.16661667Избегайте завершения процессов16681669> Использование метода [`Process.terminate`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.terminate) для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), используемые в данный момент процессом, станут повреждёнными или недоступными для других процессов.1670>1671> Поэтому, вероятно, лучше всего рассматривать использование [`Process.terminate`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.terminate) только для тех процессов, которые никогда не используют никаких разделяемых ресурсов.16721673Присоединение процессов, использующих очереди16741675> Имейте в виду, что процесс, поместивший элементы в очередь, будет ожидать перед завершением, пока все буферизированные элементы не будут переданы потоком-«питателем» в нижележащий канал. (Дочерний процесс может вызвать метод [`Queue.cancel_join_thread`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread) очереди, чтобы избежать такого поведения.)1676>1677> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещенные в очередь, в конечном итоге будут удалены до того, как процесс будет присоединен (joined). В противном случае нельзя быть уверенным, что процессы, которые помещали элементы в очередь, завершатся. Также помните, что недемонические процессы будут присоединены автоматически.1678>1679> Пример, который приведёт к взаимоблокировке:1680>1681> ```python1682> from multiprocessing import Process, Queue1683>1684> def f(q):1685> q.put('X' * 1000000)1686>1687> if __name__ == '__main__':1688> queue = Queue()1689> p = Process(target=f, args=(queue,))1690> p.start()1691> p.join() # это приводит к взаимоблокировке1692> obj = queue.get()1693> ```1694>1695> Исправлением здесь была бы перестановка двух последних строк (или просто удаление строки с `p.join()`).16961697Явная передача ресурсов дочерним процессам16981699> В Unix дочерний процесс может использовать общий ресурс, созданный в родительском процессе, с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.1700>1701> Помимо обеспечения (потенциальной) совместимости с Windows, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если какой-либо ресурс освобождается при сборке мусора в родительском процессе.1702>1703> Так, например1704>1705> ```python1706> from multiprocessing import Process, Lock1707>1708> def f():1709> ... do something using "lock" ...1710>1711> if __name__ == '__main__':1712> lock = Lock()1713> for i in range(10):1714> Process(target=f).start()1715> ```1716>1717> следует переписать как1718>1719> ```python1720> from multiprocessing import Process, Lock1721>1722> def f(l):1723> ... do something using "l" ...1724>1725> if __name__ == '__main__':1726> lock = Lock()1727> for i in range(10):1728> Process(target=f, args=(lock,)).start()1729> ```17301731Остерегайтесь замены [`sys.stdin`](https://python-all.ru/3.3/library/sys.html#sys.stdin) на «объект, подобный файлу».17321733> [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) изначально безусловно вызывал:1734>1735> ```python1736> os.close(sys.stdin.fileno())1737> ```1738>1739> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:1740>1741> ```python1742> sys.stdin.close()1743> sys.stdin = open(os.devnull)1744> ```1745>1746> Это решает фундаментальную проблему конфликта процессов друг с другом, приводящего к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, которые заменяют [`sys.stdin()`](https://python-all.ru/3.3/library/sys.html#sys.stdin) на «объект, подобный файлу» с буферизацией вывода. Эта опасность заключается в том, что если несколько процессов вызовут [`close()`](https://python-all.ru/3.3/library/io.html#io.IOBase.close) для этого объекта, одни и те же данные могут быть сброшены в него несколько раз, что приведёт к повреждению.1747>1748> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:1749>1750> ```python1751> @property1752> def cache(self):1753> pid = os.getpid()1754> if pid != self._pid:1755> self._pid = pid1756> self._cache = []1757> return self._cache1758> ```1759>1760> Дополнительную информацию см. в [issue 5155](https://python-all.ru/3.3/library/multiprocessing.html), [issue 5313](https://python-all.ru/3.3/library/multiprocessing.html) и [issue 5331](https://python-all.ru/3.3/library/multiprocessing.html).17611762### 17.2.3.2. Windows17631764Поскольку в Windows нет [`os.fork()`](https://python-all.ru/3.3/library/os.html#os.fork), существуют несколько дополнительных ограничений:17651766Упаковываемость17671768> Убедитесь, что все аргументы `Process.__init__()` являются сериализуемыми (picklable). Это означает, в частности, что связанные или несвязанные методы нельзя использовать напрямую в качестве аргумента `target` в Windows – просто определите функцию и используйте ее вместо этого.1769>1770> Кроме того, при создании подкласса [`Process`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process) необходимо убедиться, что экземпляры будут сериализуемы с помощью pickle при вызове метода [`Process.start`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.start).17711772Глобальные переменные17731774> Имейте в виду, что если код, выполняемый в дочернем процессе, пытается получить доступ к глобальной переменной, то значение, которое он видит (если таковое имеется), может не совпадать со значением в родительском процессе на момент вызова [`Process.start`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.Process.start).1775>1776> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.17771778Безопасный импорт главного модуля17791780> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).1781>1782> Например, в Windows запуск следующего модуля завершится ошибкой [`RuntimeError`](https://python-all.ru/3.3/library/exceptions.html#RuntimeError):1783>1784> ```python1785> from multiprocessing import Process1786>1787> def foo():1788> print('hello')1789>1790> p = Process(target=foo)1791> p.start()1792> ```1793>1794> Вместо этого следует защитить «точку входа» программы, используя конструкцию `if __name__ == '__main__':` следующим образом:1795>1796> ```python1797> from multiprocessing import Process, freeze_support1798>1799> def foo():1800> print('hello')1801>1802> if __name__ == '__main__':1803> freeze_support()1804> p = Process(target=foo)1805> p.start()1806> ```1807>1808> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не в замороженном виде.)1809>1810> Это позволяет новому порождённому интерпретатору Python безопасно импортировать модуль, а затем выполнить функцию `foo()` этого модуля.1811>1812> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.18131814## 17.2.4. Примеры18151816Демонстрация создания и использования настраиваемых менеджеров и прокси:18171818```python1819#1820# Этот модуль показывает, как использовать произвольные вызываемые объекты с подклассом `BaseManager`.1821# `BaseManager`.1822#1823# Авторские права (c) 2006-2008, R Oudkerk1824# Все права защищены.1825#18261827from multiprocessing import freeze_support1828from multiprocessing.managers import BaseManager, BaseProxy1829import operator18301831##18321833class Foo:1834 def f(self):1835 print('you called Foo.f()')1836 def g(self):1837 print('you called Foo.g()')1838 def _h(self):1839 print('you called Foo._h()')18401841# Простая функция-генератор1842def baz():1843 for i in range(10):1844 yield i*i18451846# Тип прокси для объектов-генераторов1847class GeneratorProxy(BaseProxy):1848 _exposed_ = ('next', '__next__')1849 def __iter__(self):1850 return self1851 def __next__(self):1852 return self._callmethod('next')1853 def __next__(self):1854 return self._callmethod('__next__')18551856# Функция для возврата модуля operator1857def get_operator_module():1858 return operator18591860##18611862class MyManager(BaseManager):1863 pass18641865# зарегистрировать класс Foo; сделать `f()` и `g()` доступными через прокси1866MyManager.register('Foo1', Foo)18671868# зарегистрировать класс Foo; сделать `g()` и `_h()` доступными через прокси1869MyManager.register('Foo2', Foo, exposed=('g', '_h'))18701871# зарегистрировать функцию-генератор baz; использовать `GeneratorProxy` для создания прокси1872MyManager.register('baz', baz, proxytype=GeneratorProxy)18731874# зарегистрировать get_operator_module(); сделать публичные функции доступными через прокси1875MyManager.register('operator', get_operator_module)18761877##18781879def test():1880 manager = MyManager()1881 manager.start()18821883 print('-' * 20)18841885 f1 = manager.Foo1()1886 f1.f()1887 f1.g()1888 assert not hasattr(f1, '_h')1889 assert sorted(f1._exposed_) == sorted(['f', 'g'])18901891 print('-' * 20)18921893 f2 = manager.Foo2()1894 f2.g()1895 f2._h()1896 assert not hasattr(f2, 'f')1897 assert sorted(f2._exposed_) == sorted(['g', '_h'])18981899 print('-' * 20)19001901 it = manager.baz()1902 for i in it:1903 print('<%d>' % i, end=' ')1904 print()19051906 print('-' * 20)19071908 op = manager.operator()1909 print('op.add(23, 45) =', op.add(23, 45))1910 print('op.pow(2, 94) =', op.pow(2, 94))1911 print('op.getslice(range(10), 2, 6) =', op.getslice(list(range(10)), 2, 6))1912 print('op.repeat(range(5), 3) =', op.repeat(list(range(5)), 3))1913 print('op._exposed_ =', op._exposed_)19141915##19161917if __name__ == '__main__':1918 freeze_support()1919 test()1920```19211922Использование [`Pool`](https://python-all.ru/3.3/library/multiprocessing.html#multiprocessing.pool.Pool):19231924```python1925#1926# Тест класса `multiprocessing.Pool`1927#1928# Авторское право (c) 2006-2008, R Oudkerk1929# Все права защищены.1930#19311932import multiprocessing1933import time1934import random1935import sys19361937#1938# Функции, используемые тестовым кодом1939#19401941def calculate(func, args):1942 result = func(*args)1943 return '%s says that %s%s = %s' % (1944 multiprocessing.current_process().name,1945 func.__name__, args, result1946 )19471948def calculatestar(args):1949 return calculate(*args)19501951def mul(a, b):1952 time.sleep(0.5 * random.random())1953 return a * b19541955def plus(a, b):1956 time.sleep(0.5 * random.random())1957 return a + b19581959def f(x):1960 return 1.0 / (x - 5.0)19611962def pow3(x):1963 return x ** 319641965def noop(x):1966 pass19671968#1969# Тестовый код1970#19711972def test():1973 print('cpu_count() = %d\n' % multiprocessing.cpu_count())19741975 #1976 # Создать пул1977 #19781979 PROCESSES = 41980 print('Creating pool with %d processes\n' % PROCESSES)1981 pool = multiprocessing.Pool(PROCESSES)1982 print('pool = %s' % pool)1983 print()19841985 #1986 # Тесты1987 #19881989 TASKS = [(mul, (i, 7)) for i in range(10)] + \1990 [(plus, (i, 8)) for i in range(10)]19911992 results = [pool.apply_async(calculate, t) for t in TASKS]1993 imap_it = pool.imap(calculatestar, TASKS)1994 imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)19951996 print('Ordered results using pool.apply_async():')1997 for r in results:1998 print('\t', r.get())1999 print()20002001 print('Ordered results using pool.imap():')2002 for x in imap_it:2003 print('\t', x)2004 print()20052006 print('Unordered results using pool.imap_unordered():')2007 for x in imap_unordered_it:2008 print('\t', x)2009 print()20102011 print('Ordered results using pool.map() --- will block till complete:')2012 for x in pool.map(calculatestar, TASKS):2013 print('\t', x)2014 print()20152016 #2017 # Простые тесты производительности2018 #20192020 N = 1000002021 print('def pow3(x): return x**3')20222023 t = time.time()2024 A = list(map(pow3, range(N)))2025 print('\tmap(pow3, range(%d)):\n\t\t%s seconds' % \2026 (N, time.time() - t))20272028 t = time.time()2029 B = pool.map(pow3, range(N))2030 print('\tpool.map(pow3, range(%d)):\n\t\t%s seconds' % \2031 (N, time.time() - t))20322033 t = time.time()2034 C = list(pool.imap(pow3, range(N), chunksize=N//8))2035 print('\tlist(pool.imap(pow3, range(%d), chunksize=%d)):\n\t\t%s' \2036 ' seconds' % (N, N//8, time.time() - t))20372038 assert A == B == C, (len(A), len(B), len(C))2039 print()20402041 L = [None] * 10000002042 print('def noop(x): pass')2043 print('L = [None] * 1000000')20442045 t = time.time()2046 A = list(map(noop, L))2047 print('\tmap(noop, L):\n\t\t%s seconds' % \2048 (time.time() - t))20492050 t = time.time()2051 B = pool.map(noop, L)2052 print('\tpool.map(noop, L):\n\t\t%s seconds' % \2053 (time.time() - t))20542055 t = time.time()2056 C = list(pool.imap(noop, L, chunksize=len(L)//8))2057 print('\tlist(pool.imap(noop, L, chunksize=%d)):\n\t\t%s seconds' % \2058 (len(L)//8, time.time() - t))20592060 assert A == B == C, (len(A), len(B), len(C))2061 print()20622063 del A, B, C, L20642065 #2066 # Тестирование обработки ошибок2067 #20682069 print('Testing error handling:')20702071 try:2072 print(pool.apply(f, (5,)))2073 except ZeroDivisionError:2074 print('\tGot ZeroDivisionError as expected from pool.apply()')2075 else:2076 raise AssertionError('expected ZeroDivisionError')20772078 try:2079 print(pool.map(f, list(range(10))))2080 except ZeroDivisionError:2081 print('\tGot ZeroDivisionError as expected from pool.map()')2082 else:2083 raise AssertionError('expected ZeroDivisionError')20842085 try:2086 print(list(pool.imap(f, list(range(10)))))2087 except ZeroDivisionError:2088 print('\tGot ZeroDivisionError as expected from list(pool.imap())')2089 else:2090 raise AssertionError('expected ZeroDivisionError')20912092 it = pool.imap(f, list(range(10)))2093 for i in range(10):2094 try:2095 x = next(it)2096 except ZeroDivisionError:2097 if i == 5:2098 pass2099 except StopIteration:2100 break2101 else:2102 if i == 5:2103 raise AssertionError('expected ZeroDivisionError')21042105 assert i == 92106 print('\tGot ZeroDivisionError as expected from IMapIterator.next()')2107 print()21082109 #2110 # Тестирование тайм-аутов2111 #21122113 print('Testing ApplyResult.get() with timeout:', end=' ')2114 res = pool.apply_async(calculate, TASKS[0])2115 while 1:2116 sys.stdout.flush()2117 try:2118 sys.stdout.write('\n\t%s' % res.get(0.02))2119 break2120 except multiprocessing.TimeoutError:2121 sys.stdout.write('.')2122 print()2123 print()21242125 print('Testing IMapIterator.next() with timeout:', end=' ')2126 it = pool.imap(calculatestar, TASKS)2127 while 1:2128 sys.stdout.flush()2129 try:2130 sys.stdout.write('\n\t%s' % it.next(0.02))2131 except StopIteration:2132 break2133 except multiprocessing.TimeoutError:2134 sys.stdout.write('.')2135 print()2136 print()21372138 #2139 # Тестирование колбэка2140 #21412142 print('Testing callback:')21432144 A = []2145 B = [56, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]21462147 r = pool.apply_async(mul, (7, 8), callback=A.append)2148 r.wait()21492150 r = pool.map_async(pow3, list(range(10)), callback=A.extend)2151 r.wait()21522153 if A == B:2154 print('\tcallbacks succeeded\n')2155 else:2156 print('\t*** callbacks failed\n\t\t%s != %s\n' % (A, B))21572158 #2159 # Проверить, что нет незавершённых задач2160 #21612162 assert not pool._cache, 'cache = %r' % pool._cache21632164 #2165 # Проверить методы close()2166 #21672168 print('Testing close():')21692170 for worker in pool._pool:2171 assert worker.is_alive()21722173 result = pool.apply_async(time.sleep, [0.5])2174 pool.close()2175 pool.join()21762177 assert result.get() is None21782179 for worker in pool._pool:2180 assert not worker.is_alive()21812182 print('\tclose() succeeded\n')21832184 #2185 # Проверить метод terminate()2186 #21872188 print('Testing terminate():')21892190 pool = multiprocessing.Pool(2)2191 DELTA = 0.12192 ignore = pool.apply(pow3, [2])2193 results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]2194 pool.terminate()2195 pool.join()21962197 for worker in pool._pool:2198 assert not worker.is_alive()21992200 print('\tterminate() succeeded\n')22012202 #2203 # Проверить сборку мусора2204 #22052206 print('Testing garbage collection:')22072208 pool = multiprocessing.Pool(2)2209 DELTA = 0.12210 processes = pool._pool2211 ignore = pool.apply(pow3, [2])2212 results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]22132214 results = pool = None22152216 time.sleep(DELTA * 2)22172218 for worker in processes:2219 assert not worker.is_alive()22202221 print('\tgarbage collection succeeded\n')22222223if __name__ == '__main__':2224 multiprocessing.freeze_support()22252226 assert len(sys.argv) in (1, 2)22272228 if len(sys.argv) == 1 or sys.argv[1] == 'processes':2229 print(' Using processes '.center(79, '-'))2230 elif sys.argv[1] == 'threads':2231 print(' Using threads '.center(79, '-'))2232 import multiprocessing.dummy as multiprocessing2233 else:2234 print('Usage:\n\t%s [processes | threads]' % sys.argv[0])2235 raise SystemExit(2)22362237 test()2238```22392240Типы синхронизации, такие как блокировки, условия и очереди:22412242```python2243#2244# Тестовый файл для пакета `multiprocessing`2245#2246# Авторское право (c) 2006-2008, R Oudkerk2247# Все права защищены.2248#22492250import time2251import sys2252import random2253from queue import Empty22542255import multiprocessing # может быть перезаписан22562257#### TEST_VALUE22582259def value_func(running, mutex):2260 random.seed()2261 time.sleep(random.random()*4)22622263 mutex.acquire()2264 print('\n\t\t\t' + str(multiprocessing.current_process()) + ' has finished')2265 running.value -= 12266 mutex.release()22672268def test_value():2269 TASKS = 102270 running = multiprocessing.Value('i', TASKS)2271 mutex = multiprocessing.Lock()22722273 for i in range(TASKS):2274 p = multiprocessing.Process(target=value_func, args=(running, mutex))2275 p.start()22762277 while running.value > 0:2278 time.sleep(0.08)2279 mutex.acquire()2280 print(running.value, end=' ')2281 sys.stdout.flush()2282 mutex.release()22832284 print()2285 print('No more running processes')22862287#### TEST_QUEUE22882289def queue_func(queue):2290 for i in range(30):2291 time.sleep(0.5 * random.random())2292 queue.put(i*i)2293 queue.put('STOP')22942295def test_queue():2296 q = multiprocessing.Queue()22972298 p = multiprocessing.Process(target=queue_func, args=(q,))2299 p.start()23002301 o = None2302 while o != 'STOP':2303 try:2304 o = q.get(timeout=0.3)2305 print(o, end=' ')2306 sys.stdout.flush()2307 except Empty:2308 print('TIMEOUT')23092310 print()23112312#### TEST_CONDITION23132314def condition_func(cond):2315 cond.acquire()2316 print('\t' + str(cond))2317 time.sleep(2)2318 print('\tchild is notifying')2319 print('\t' + str(cond))2320 cond.notify()2321 cond.release()23222323def test_condition():2324 cond = multiprocessing.Condition()23252326 p = multiprocessing.Process(target=condition_func, args=(cond,))2327 print(cond)23282329 cond.acquire()2330 print(cond)2331 cond.acquire()2332 print(cond)23332334 p.start()23352336 print('main is waiting')2337 cond.wait()2338 print('main has woken up')23392340 print(cond)2341 cond.release()2342 print(cond)2343 cond.release()23442345 p.join()2346 print(cond)23472348#### TEST_SEMAPHORE23492350def semaphore_func(sema, mutex, running):2351 sema.acquire()23522353 mutex.acquire()2354 running.value += 12355 print(running.value, 'tasks are running')2356 mutex.release()23572358 random.seed()2359 time.sleep(random.random()*2)23602361 mutex.acquire()2362 running.value -= 12363 print('%s has finished' % multiprocessing.current_process())2364 mutex.release()23652366 sema.release()23672368def test_semaphore():2369 sema = multiprocessing.Semaphore(3)2370 mutex = multiprocessing.RLock()2371 running = multiprocessing.Value('i', 0)23722373 processes = [2374 multiprocessing.Process(target=semaphore_func,2375 args=(sema, mutex, running))2376 for i in range(10)2377 ]23782379 for p in processes:2380 p.start()23812382 for p in processes:2383 p.join()23842385#### TEST_JOIN_TIMEOUT23862387def join_timeout_func():2388 print('\tchild sleeping')2389 time.sleep(5.5)2390 print('\n\tchild terminating')23912392def test_join_timeout():2393 p = multiprocessing.Process(target=join_timeout_func)2394 p.start()23952396 print('waiting for process to finish')23972398 while 1:2399 p.join(timeout=1)2400 if not p.is_alive():2401 break2402 print('.', end=' ')2403 sys.stdout.flush()24042405#### TEST_EVENT24062407def event_func(event):2408 print('\t%r is waiting' % multiprocessing.current_process())2409 event.wait()2410 print('\t%r has woken up' % multiprocessing.current_process())24112412def test_event():2413 event = multiprocessing.Event()24142415 processes = [multiprocessing.Process(target=event_func, args=(event,))2416 for i in range(5)]24172418 for p in processes:2419 p.start()24202421 print('main is sleeping')2422 time.sleep(2)24232424 print('main is setting event')2425 event.set()24262427 for p in processes:2428 p.join()24292430#### TEST_SHAREDVALUES24312432def sharedvalues_func(values, arrays, shared_values, shared_arrays):2433 for i in range(len(values)):2434 v = values[i][1]2435 sv = shared_values[i].value2436 assert v == sv24372438 for i in range(len(values)):2439 a = arrays[i][1]2440 sa = list(shared_arrays[i][:])2441 assert a == sa24422443 print('Tests passed')24442445def test_sharedvalues():2446 values = [2447 ('i', 10),2448 ('h', -2),2449 ('d', 1.25)2450 ]2451 arrays = [2452 ('i', list(range(100))),2453 ('d', [0.25 * i for i in range(100)]),2454 ('H', list(range(1000)))2455 ]24562457 shared_values = [multiprocessing.Value(id, v) for id, v in values]2458 shared_arrays = [multiprocessing.Array(id, a) for id, a in arrays]24592460 p = multiprocessing.Process(2461 target=sharedvalues_func,2462 args=(values, arrays, shared_values, shared_arrays)2463 )2464 p.start()2465 p.join()24662467 assert p.exitcode == 024682469####24702471def test(namespace=multiprocessing):2472 global multiprocessing24732474 multiprocessing = namespace24752476 for func in [test_value, test_queue, test_condition,2477 test_semaphore, test_join_timeout, test_event,2478 test_sharedvalues]:24792480 print('\n\t######## %s\n' % func.__name__)2481 func()24822483 ignore = multiprocessing.active_children() # очистка всех старых процессов2484 if hasattr(multiprocessing, '_debug_info'):2485 info = multiprocessing._debug_info()2486 if info:2487 print(info)2488 raise ValueError('there should be no positive refcounts left')24892490if __name__ == '__main__':2491 multiprocessing.freeze_support()24922493 assert len(sys.argv) in (1, 2)24942495 if len(sys.argv) == 1 or sys.argv[1] == 'processes':2496 print(' Using processes '.center(79, '-'))2497 namespace = multiprocessing2498 elif sys.argv[1] == 'manager':2499 print(' Using processes and a manager '.center(79, '-'))2500 namespace = multiprocessing.Manager()2501 namespace.Process = multiprocessing.Process2502 namespace.current_process = multiprocessing.current_process2503 namespace.active_children = multiprocessing.active_children2504 elif sys.argv[1] == 'threads':2505 print(' Using threads '.center(79, '-'))2506 import multiprocessing.dummy as namespace2507 else:2508 print('Usage:\n\t%s [processes | manager | threads]' % sys.argv[0])2509 raise SystemExit(2)25102511 test(namespace)2512```25132514Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:25152516```python2517#2518# Простой пример использования пула рабочих процессов для выполнения задач.2519#2520# Обратите внимание, что результаты, скорее всего, не будут получены из выходной2521# очереди в том же порядке, в котором соответствующие задачи были2522# помещены во входную очередь. Если важно получить результаты2523# в исходном порядке, рассмотрите использование `Pool.map()` или2524# `Pool.imap()` (что в любом случае сократит объём необходимого кода).2525#2526# Авторское право (c) 2006-2008, R Oudkerk2527# Все права защищены.2528#25292530import time2531import random25322533from multiprocessing import Process, Queue, current_process, freeze_support25342535#2536# Функция, выполняемая рабочими процессами2537#25382539def worker(input, output):2540 for func, args in iter(input.get, 'STOP'):2541 result = calculate(func, args)2542 output.put(result)25432544#2545# Функция, используемая для вычисления результата2546#25472548def calculate(func, args):2549 result = func(*args)2550 return '%s says that %s%s = %s' % \2551 (current_process().name, func.__name__, args, result)25522553#2554# Функции, на которые ссылаются задачи2555#25562557def mul(a, b):2558 time.sleep(0.5*random.random())2559 return a * b25602561def plus(a, b):2562 time.sleep(0.5*random.random())2563 return a + b25642565#2566#2567#25682569def test():2570 NUMBER_OF_PROCESSES = 42571 TASKS1 = [(mul, (i, 7)) for i in range(20)]2572 TASKS2 = [(plus, (i, 8)) for i in range(10)]25732574 # Создание очередей2575 task_queue = Queue()2576 done_queue = Queue()25772578 # Отправка задач2579 for task in TASKS1:2580 task_queue.put(task)25812582 # Запуск рабочих процессов2583 for i in range(NUMBER_OF_PROCESSES):2584 Process(target=worker, args=(task_queue, done_queue)).start()25852586 # Получение и вывод результатов2587 print('Unordered results:')2588 for i in range(len(TASKS1)):2589 print('\t', done_queue.get())25902591 # Добавить ещё задач с помощью `put()`2592 for task in TASKS2:2593 task_queue.put(task)25942595 # Получить и вывести ещё несколько результатов2596 for i in range(len(TASKS2)):2597 print('\t', done_queue.get())25982599 # Сообщить дочерним процессам об остановке2600 for i in range(NUMBER_OF_PROCESSES):2601 task_queue.put('STOP')26022603if __name__ == '__main__':2604 freeze_support()2605 test()2606```26072608Пример того, как пул рабочих процессов может запускать по одному экземпляру [`SimpleHTTPRequestHandler`](https://python-all.ru/3.3/library/http.server.html#http.server.SimpleHTTPRequestHandler), разделяя один слушающий сокет.26092610```python2611#2612# Пример, в котором пул HTTP-серверов использует один общий слушающий сокет2613#2614# В Windows этот модуль зависит от возможности сериализации сокета с помощью pickle2615# объекта, чтобы рабочие процессы могли унаследовать копию сервера2616# объект. (Мы импортируем `multiprocessing.reduction`, чтобы включить эту сериализацию.)2617#2618# Не уверены, нужно ли синхронизировать доступ к методу `socket.accept()` с помощью2619# блокировки, разделяемой между процессами – похоже, это не обязательно.2620#2621# Copyright (c) 2006-2008, R Oudkerk2622# Все права защищены.2623#26242625import os2626import sys26272628from multiprocessing import Process, current_process, freeze_support2629from http.server import HTTPServer2630from http.server import SimpleHTTPRequestHandler26312632if sys.platform == 'win32':2633 import multiprocessing.reduction # сделать сокеты сериализуемыми/наследуемыми26342635def note(format, *args):2636 sys.stderr.write('[%s]\t%s\n' % (current_process().name, format % args))26372638class RequestHandler(SimpleHTTPRequestHandler):2639 # мы переопределяем log_message(), чтобы показывать, какой процесс обрабатывает запрос2640 def log_message(self, format, *args):2641 note(format, *args)26422643def serve_forever(server):2644 note('starting server')2645 try:2646 server.serve_forever()2647 except KeyboardInterrupt:2648 pass26492650def runpool(address, number_of_processes):2651 # создать единственный объект сервера – каждый дочерний процесс унаследует его копию2652 server = HTTPServer(address, RequestHandler)26532654 # создать дочерние процессы, которые будут работать как воркеры2655 for i in range(number_of_processes - 1):2656 Process(target=serve_forever, args=(server,)).start()26572658 # главный процесс также выступает в роли воркера2659 serve_forever(server)26602661def test():2662 DIR = os.path.join(os.path.dirname(__file__), '..')2663 ADDRESS = ('localhost', 8000)2664 NUMBER_OF_PROCESSES = 426652666 print('Serving at http://%s:%d using %d worker processes' % \2667 (ADDRESS[0], ADDRESS[1], NUMBER_OF_PROCESSES))2668 print('To exit press Ctrl-' + ['C', 'Break'][sys.platform=='win32'])26692670 os.chdir(DIR)2671 runpool(ADDRESS, NUMBER_OF_PROCESSES)26722673if __name__ == '__main__':2674 freeze_support()2675 test()2676```26772678Несколько простых тестов производительности, сравнивающих [`multiprocessing`](https://python-all.ru/3.3/library/multiprocessing.html#module-multiprocessing) с [`threading`](https://python-all.ru/3.3/library/threading.html#module-threading):26792680```python2681#2682# Простые тесты производительности для пакета multiprocessing2683#2684# Copyright (c) 2006-2008, R Oudkerk2685# Все права защищены.2686#26872688import time2689import multiprocessing2690import threading2691import queue2692import gc26932694_timer = time.perf_counter26952696delta = 126972698#### TEST_QUEUESPEED26992700def queuespeed_func(q, c, iterations):2701 a = '0' * 2562702 c.acquire()2703 c.notify()2704 c.release()27052706 for i in range(iterations):2707 q.put(a)27082709 q.put('STOP')27102711def test_queuespeed(Process, q, c):2712 elapsed = 02713 iterations = 127142715 while elapsed < delta:2716 iterations *= 227172718 p = Process(target=queuespeed_func, args=(q, c, iterations))2719 c.acquire()2720 p.start()2721 c.wait()2722 c.release()27232724 result = None2725 t = _timer()27262727 while result != 'STOP':2728 result = q.get()27292730 elapsed = _timer() - t27312732 p.join()27332734 print(iterations, 'objects passed through the queue in', elapsed, 'seconds')2735 print('average number/sec:', iterations/elapsed)27362737#### TEST_PIPESPEED27382739def pipe_func(c, cond, iterations):2740 a = '0' * 2562741 cond.acquire()2742 cond.notify()2743 cond.release()27442745 for i in range(iterations):2746 c.send(a)27472748 c.send('STOP')27492750def test_pipespeed():2751 c, d = multiprocessing.Pipe()2752 cond = multiprocessing.Condition()2753 elapsed = 02754 iterations = 127552756 while elapsed < delta:2757 iterations *= 227582759 p = multiprocessing.Process(target=pipe_func,2760 args=(d, cond, iterations))2761 cond.acquire()2762 p.start()2763 cond.wait()2764 cond.release()27652766 result = None2767 t = _timer()27682769 while result != 'STOP':2770 result = c.recv()27712772 elapsed = _timer() - t2773 p.join()27742775 print(iterations, 'objects passed through connection in',elapsed,'seconds')2776 print('average number/sec:', iterations/elapsed)27772778#### TEST_SEQSPEED27792780def test_seqspeed(seq):2781 elapsed = 02782 iterations = 127832784 while elapsed < delta:2785 iterations *= 227862787 t = _timer()27882789 for i in range(iterations):2790 a = seq[5]27912792 elapsed = _timer() - t27932794 print(iterations, 'iterations in', elapsed, 'seconds')2795 print('average number/sec:', iterations/elapsed)27962797#### TEST_LOCK27982799def test_lockspeed(l):2800 elapsed = 02801 iterations = 128022803 while elapsed < delta:2804 iterations *= 228052806 t = _timer()28072808 for i in range(iterations):2809 l.acquire()2810 l.release()28112812 elapsed = _timer() - t28132814 print(iterations, 'iterations in', elapsed, 'seconds')2815 print('average number/sec:', iterations/elapsed)28162817#### TEST_CONDITION28182819def conditionspeed_func(c, N):2820 c.acquire()2821 c.notify()28222823 for i in range(N):2824 c.wait()2825 c.notify()28262827 c.release()28282829def test_conditionspeed(Process, c):2830 elapsed = 02831 iterations = 128322833 while elapsed < delta:2834 iterations *= 228352836 c.acquire()2837 p = Process(target=conditionspeed_func, args=(c, iterations))2838 p.start()28392840 c.wait()28412842 t = _timer()28432844 for i in range(iterations):2845 c.notify()2846 c.wait()28472848 elapsed = _timer() - t28492850 c.release()2851 p.join()28522853 print(iterations * 2, 'waits in', elapsed, 'seconds')2854 print('average number/sec:', iterations * 2 / elapsed)28552856####28572858def test():2859 manager = multiprocessing.Manager()28602861 gc.disable()28622863 print('\n\t######## testing Queue.Queue\n')2864 test_queuespeed(threading.Thread, queue.Queue(),2865 threading.Condition())2866 print('\n\t######## testing multiprocessing.Queue\n')2867 test_queuespeed(multiprocessing.Process, multiprocessing.Queue(),2868 multiprocessing.Condition())2869 print('\n\t######## testing Queue managed by server process\n')2870 test_queuespeed(multiprocessing.Process, manager.Queue(),2871 manager.Condition())2872 print('\n\t######## testing multiprocessing.Pipe\n')2873 test_pipespeed()28742875 print()28762877 print('\n\t######## testing list\n')2878 test_seqspeed(list(range(10)))2879 print('\n\t######## testing list managed by server process\n')2880 test_seqspeed(manager.list(list(range(10))))2881 print('\n\t######## testing Array("i", ..., lock=False)\n')2882 test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=False))2883 print('\n\t######## testing Array("i", ..., lock=True)\n')2884 test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=True))28852886 print()28872888 print('\n\t######## testing threading.Lock\n')2889 test_lockspeed(threading.Lock())2890 print('\n\t######## testing threading.RLock\n')2891 test_lockspeed(threading.RLock())2892 print('\n\t######## testing multiprocessing.Lock\n')2893 test_lockspeed(multiprocessing.Lock())2894 print('\n\t######## testing multiprocessing.RLock\n')2895 test_lockspeed(multiprocessing.RLock())2896 print('\n\t######## testing lock managed by server process\n')2897 test_lockspeed(manager.Lock())2898 print('\n\t######## testing rlock managed by server process\n')2899 test_lockspeed(manager.RLock())29002901 print()29022903 print('\n\t######## testing threading.Condition\n')2904 test_conditionspeed(threading.Thread, threading.Condition())2905 print('\n\t######## testing multiprocessing.Condition\n')2906 test_conditionspeed(multiprocessing.Process, multiprocessing.Condition())2907 print('\n\t######## testing condition managed by a server process\n')2908 test_conditionspeed(multiprocessing.Process, manager.Condition())29092910 gc.enable()29112912if __name__ == '__main__':2913 multiprocessing.freeze_support()2914 test()2915```2916