multiprocessing.md
1> **Источник:** https://python-all.ru/3.4/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.4/library/multiprocessing.html#module-multiprocessing) – Процессно-ориентированный параллелизм89## 17.2.1. Введение1011Пакет [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) поддерживает порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/3.4/library/threading.html#module-threading). Пакет [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) предоставляет как локальный, так и удалённый параллелизм, обходя [*Глобальную блокировку интерпретатора*](https://python-all.ru/3.4/glossary.html#term-global-interpreter-lock) с помощью подпроцессов вместо потоков. Благодаря этому модуль [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) позволяет программисту полностью задействовать несколько процессоров на данной машине. Он работает как на Unix, так и на Windows.1213Модуль [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) также вводит API, не имеющие аналогов в модуле [`threading`](https://python-all.ru/3.4/library/threading.html#module-threading). Ярким примером этого является объект [`Pool`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool), предоставляющий удобные средства для параллельного выполнения функции для нескольких входных значений, распределяя входные данные по процессам (распараллеливание данных). В следующем примере показан общепринятый способ определения таких функций в модуле, чтобы дочерние процессы могли успешно импортировать этот модуль. Этот базовый пример распараллеливания данных с помощью [`Pool`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool),1415```python16from multiprocessing import Pool1718def f(x):19 return x*x2021if __name__ == '__main__':22 with Pool(5) as p:23 print(p.map(f, [1, 2, 3]))24```2526будет выводить на стандартный вывод2728```python29[1, 4, 9]30```3132### 17.2.1.1. Класс [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process)3334В [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) процессы порождаются созданием объекта [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.start). [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process) следует API [`threading.Thread`](https://python-all.ru/3.4/library/threading.html#threading.Thread). Пример простой многопроцессной программы:3536```python37from multiprocessing import Process3839def f(name):40 print('hello', name)4142if __name__ == '__main__':43 p = Process(target=f, args=('bob',))44 p.start()45 p.join()46```4748Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:4950```python51from multiprocessing import Process52import os5354def info(title):55 print(title)56 print('module name:', __name__)57 print('parent process:', os.getppid())58 print('process id:', os.getpid())5960def f(name):61 info('function f')62 print('hello', name)6364if __name__ == '__main__':65 info('main line')66 p = Process(target=f, args=('bob',))67 p.start()68 p.join()69```7071Объяснение, зачем нужна часть `if __name__ == '__main__'`, см. в разделе [*Рекомендации по программированию*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-programming).7273### 17.2.1.2. Контексты и методы запуска7475В зависимости от платформы [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) поддерживает три способа запуска процесса. Эти *способы запуска*:7677> ***spawn***78>79> Родительский процесс запускает новый интерпретатор Python. Дочерний процесс наследует только те ресурсы, которые необходимы для выполнения метода [`run()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.run) объектов процесса. В частности, ненужные файловые дескрипторы и дескрипторы родительского процесса не наследуются. Запуск процесса этим способом довольно медленный по сравнению с использованием *fork* или *forkserver*.80>81> Доступен на Unix и Windows. По умолчанию на Windows.82>83> ***fork***84>85> Родительский процесс использует [`os.fork()`](https://python-all.ru/3.4/library/os.html#os.fork) для порождения копии интерпретатора Python. Дочерний процесс при запуске практически идентичен родительскому. Все ресурсы родителя наследуются дочерним процессом. Стоит отметить, что безопасное порождение многопоточного процесса проблематично.86>87> Доступно только на Unix. По умолчанию используется на Unix.88>89> ***forkserver***90>91> Когда программа запускается и выбирает способ запуска *forkserver*, запускается серверный процесс. После этого, когда требуется новый процесс, родительский процесс подключается к серверу и запрашивает порождение нового процесса. Процесс-сервер fork является однопоточным, поэтому для него безопасно использовать [`os.fork()`](https://python-all.ru/3.4/library/os.html#os.fork). Лишние ресурсы не наследуются.92>93> Доступно на платформах Unix, которые поддерживают передачу файловых дескрипторов через каналы Unix.9495Изменено в версии 3.4: *spawn* добавлен на всех платформах Unix, а *forkserver* добавлен для некоторых платформ Unix. Дочерние процессы больше не наследуют все наследуемые дескрипторы родительского процесса в Windows.9697В Unix при использовании методов запуска *spawn* или *forkserver* также будет запущен процесс *отслеживателя семафоров*, который отслеживает отсоединённые именованные семафоры, созданные процессами программы. Когда все процессы завершатся, отслеживатель семафоров отсоединяет все оставшиеся семафоры. Обычно их не должно быть, но если процесс был убит сигналом, могут остаться «утекшие» семафоры. (Отсоединение именованных семафоров – серьёзное дело, так как система допускает лишь ограниченное количество, и они не будут автоматически отсоединены до следующей перезагрузки.)9899Чтобы выбрать способ запуска, используется [`set_start_method()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.set_start_method) в блоке `if __name__ == '__main__'` главного модуля. Например:100101```python102import multiprocessing as mp103104def foo(q):105 q.put('hello')106107if __name__ == '__main__':108 mp.set_start_method('spawn')109 q = mp.Queue()110 p = mp.Process(target=foo, args=(q,))111 p.start()112 print(q.get())113 p.join()114```115116[`set_start_method()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.set_start_method) не следует использовать в программе более одного раза.117118В качестве альтернативы можно использовать [`get_context()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.get_context) для получения объекта контекста. Объекты контекста имеют тот же API, что и модуль multiprocessing, и позволяют использовать несколько способов запуска в одной программе.119120```python121import multiprocessing as mp122123def foo(q):124 q.put('hello')125126if __name__ == '__main__':127 ctx = mp.get_context('spawn')128 q = ctx.Queue()129 p = ctx.Process(target=foo, args=(q,))130 p.start()131 print(q.get())132 p.join()133```134135Обратите внимание, что объекты, относящиеся к одному контексту, могут быть несовместимы с процессами другого контекста. В частности, блокировки, созданные с помощью контекста *fork*, не могут быть переданы процессам, запущенным методами запуска *spawn* или *forkserver*.136137Библиотека, которая хочет использовать определённый способ запуска, вероятно, должна использовать [`get_context()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.get_context), чтобы не мешать выбору пользователя библиотеки.138139### 17.2.1.3. Обмен объектами между процессами140141[`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) поддерживает два типа каналов связи между процессами:142143**Очереди**144145> Класс [`Queue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue) является почти копией [`queue.Queue`](https://python-all.ru/3.4/library/queue.html#queue.Queue). Например:146>147> ```python148> from multiprocessing import Process, Queue149>150> def f(q):151> q.put([42, None, 'hello'])152>153> if __name__ == '__main__':154> q = Queue()155> p = Process(target=f, args=(q,))156> p.start()157> print(q.get()) # печатает "[42, None, 'hello']"158> p.join()159> ```160>161> Очереди потокобезопасны и безопасны для процессов.162163**Каналы**164165> Функция [`Pipe()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является двунаправленным. Например:166>167> ```python168> from multiprocessing import Process, Pipe169>170> def f(conn):171> conn.send([42, None, 'hello'])172> conn.close()173>174> if __name__ == '__main__':175> parent_conn, child_conn = Pipe()176> p = Process(target=f, args=(child_conn,))177> p.start()178> print(parent_conn.recv()) # печатает "[42, None, 'hello']"179> p.join()180> ```181>182> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы [`send()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.send) и [`recv()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.recv) (среди прочих). Следует учитывать, что данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.183184### 17.2.1.4. Синхронизация между процессами185186[`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) содержит эквиваленты всех примитивов синхронизации из [`threading`](https://python-all.ru/3.4/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс одновременно выводит на стандартный вывод:187188```python189from multiprocessing import Process, Lock190191def f(l, i):192 l.acquire()193 try:194 print('hello world', i)195 finally:196 l.release()197198if __name__ == '__main__':199 lock = Lock()200201 for num in range(10):202 Process(target=f, args=(lock, num)).start()203```204205Без использования блокировки вывод от разных процессов может полностью перепутаться.206207### 17.2.1.5. Совместное использование состояния между процессами208209Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.210211Однако, если действительно необходимо использовать общие данные, [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) предоставляет несколько способов это сделать.212213**Разделяемая память**214215> Данные могут храниться в карте разделяемой памяти с помощью [`Value`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Array). Например, следующий код216>217> ```python218> from multiprocessing import Process, Value, Array219>220> def f(n, a):221> n.value = 3.1415927222> for i in range(len(a)):223> a[i] = -a[i]224>225> if __name__ == '__main__':226> num = Value('d', 0.0)227> arr = Array('i', range(10))228>229> p = Process(target=f, args=(num, arr))230> p.start()231> p.join()232>233> print(num.value)234> print(arr[:])235> ```236>237> выведет238>239> ```python240> 3.1415927241> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]242> ```243>244> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, как в модуле [`array`](https://python-all.ru/3.4/library/array.html#module-array): `'d'` обозначает число двойной точности с плавающей точкой, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасны для работы с процессами и потоками.245>246> Для большей гибкости при использовании разделяемой памяти можно воспользоваться модулем [`multiprocessing.sharedctypes`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing.sharedctypes), который поддерживает создание произвольных объектов ctypes, размещённых в разделяемой памяти.247248**Серверный процесс**249250> Объект менеджера, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими через прокси.251>252> Менеджер, возвращаемый `Manager()`, поддерживает типы [`list`](https://python-all.ru/3.4/library/stdtypes.html#list), [`dict`](https://python-all.ru/3.4/library/stdtypes.html#dict), `Namespace`, [`Блокировка`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Event), [`Barrier`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Barrier), [`Очередь`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Array). Например,253>254> ```python255> from multiprocessing import Process, Manager256>257> def f(d, l):258> d[1] = '1'259> d['2'] = 2260> d[0.25] = None261> l.reverse()262>263> if __name__ == '__main__':264> with Manager() as manager:265> d = manager.dict()266> l = manager.list(range(10))267>268> p = Process(target=f, args=(d, l))269> p.start()270> p.join()271>272> print(d)273> print(l)274> ```275>276> выведет277>278> ```python279> {0.25: None, 1: '1', '2': 2}280> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]281> ```282>283> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.284285### 17.2.1.6. Использование пула рабочих процессов286287Класс [`Pool`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool) представляет собой пул рабочих процессов. Он имеет методы, которые позволяют передавать задачи рабочим процессам разными способами.288289Например:290291```python292from multiprocessing import Pool293from time import sleep294295def f(x):296 return x*x297298if __name__ == '__main__':299 # запустить 4 рабочих процесса300 with Pool(processes=4) as pool:301302 # печатает "[0, 1, 4,..., 81]"303 print(pool.map(f, range(10)))304305 # печатает те же числа в произвольном порядке306 for i in pool.imap_unordered(f, range(10)):307 print(i)308309 # асинхронно вычислить "f(10)"310 res = pool.apply_async(f, [10])311 print(res.get(timeout=1)) # выводит "100"312313 # заставить worker спать 10 секунд314 res = pool.apply_async(sleep, [10])315 print(res.get(timeout=1)) # вызывает multiprocessing.TimeoutError316317 # выход из блока 'with' остановил пул318```319320Обратите внимание: методы пула должны использоваться только тем процессом, который его создал.321322> **Примечание**323>324> Для работы некоторых функций этого пакета требуется, чтобы модуль `__main__` был импортируем дочерними процессами. Это рассмотрено в разделе [*Рекомендации по программированию*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-programming), но стоит упомянуть здесь. Это означает, что некоторые примеры, например, с [`multiprocessing.pool.Pool`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool), не будут работать в интерактивном интерпретаторе. Например:325>326> ```python327> >>> from multiprocessing import Pool328> >>> p = Pool(5)329> >>> def f(x):330> ... return x*x331> ...332> >>> p.map(f, [1,2,3])333> Process PoolWorker-1:334> Process PoolWorker-2:335> Process PoolWorker-3:336> Traceback (most recent call last):337> AttributeError: 'module' object has no attribute 'f'338> AttributeError: 'module' object has no attribute 'f'339> AttributeError: 'module' object has no attribute 'f'340> ```341>342> (Если попробовать это сделать, на самом деле будут выведены три полные трассировки стека, перемешанные в полу-случайном порядке, и затем, возможно, придётся как-то остановить главный процесс.)343344## 17.2.2. Справочник345346Пакет [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) в основном повторяет API модуля [`threading`](https://python-all.ru/3.4/library/threading.html#module-threading).347348### 17.2.2.1. [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process) и исключения349350#### `class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)`351352Объекты Process представляют активность, которая выполняется в отдельном процессе. Класс [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process) содержит эквиваленты всех методов [`threading.Thread`](https://python-all.ru/3.4/library/threading.html#threading.Thread).353354Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должно быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/3.4/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию равен `None`, то есть ничего не вызывается. *name* – имя процесса (подробнее см. [`name`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.name)). *args* – кортеж аргументов для вызова целевой функции. *kwargs* – это словарь именованных аргументов для вызова целевой функции. Если указан, ключевой аргумент *daemon* устанавливает флаг процесса [`daemon`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.daemon) в `True` или `False`. Если `None` (по умолчанию), этот флаг будет наследоваться от создающего процесса.355356По умолчанию в *target* не передаётся никаких аргументов.357358Если подкласс переопределяет конструктор, он должен убедиться, что вызывает конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.359360Изменено в версии 3.3: Добавлен аргумент *daemon*.361362#### `run()`363364Метод, представляющий активность процесса.365366Вы можете переопределить этот метод в подклассе. Стандартный метод [`run()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.367368#### `start()`369370Запускает активность процесса.371372Этот метод должен вызываться не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.373374#### `join([timeout])`375376Если необязательный аргумент *timeout* равен `None` (по умолчанию), метод блокируется до завершения процесса, для которого вызывается метод [`join()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.join). Если *timeout* – положительное число, блокировка длится не более *timeout* секунд.377378Процесс может быть присоединён много раз.379380Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.381382#### `name`383384Имя процесса. Имя – это строка, используемая только для идентификации. Оно не имеет семантического значения. Несколько процессов могут иметь одно и то же имя.385386Начальное имя задается конструктором. Если конструктору не передано явное имя, создается имя вида ‘Process-N1:N2:...:Nk‘, где каждый Nk – это N-й потомок своего родителя.387388#### `is_alive()`389390Возвращает, жив ли процесс.391392Грубо говоря, объект процесса считается живым с момента возврата метода [`start()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.393394#### `daemon`395396Флаг daemon процесса, логическое значение. Должен быть установлен до вызова [`start()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.start).397398Начальное значение наследуется от создающего процесса.399400Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.401402Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.403404В дополнение к API [`threading.Thread`](https://python-all.ru/3.4/library/threading.html#threading.Thread), объекты [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process) также поддерживают следующие атрибуты и методы:405406#### `pid`407408Возвращает идентификатор процесса. До запуска процесса будет `None`.409410#### `exitcode`411412Код завершения дочернего процесса. Будет `None`, если процесс ещё не завершился. Отрицательное значение *-N* указывает, что дочерний процесс был завершён сигналом *N*.413414#### `authkey`415416Ключ аутентификации процесса (байтовая строка).417418При инициализации [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) главному процессу присваивается случайная строка с помощью [`os.urandom()`](https://python-all.ru/3.4/library/os.html#os.urandom).419420При создании объекта [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process) он наследует ключ аутентификации своего родительского процесса, хотя это можно изменить, установив [`authkey`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.authkey) в другую строку байтов.421422См. [*Ключи аутентификации*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-auth-keys).423424#### `sentinel`425426Числовой дескриптор системного объекта, который становится «готов» по завершении процесса.427428Это значение можно использовать, если нужно ожидать несколько событий одновременно с помощью [`multiprocessing.connection.wait()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.connection.wait). В противном случае проще вызвать [`join()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.join).429430В Windows это дескриптор ОС, используемый с семейством API-вызовов `WaitForSingleObject` и `WaitForMultipleObjects`. В Unix это файловый дескриптор, используемый с примитивами из модуля [`select`](https://python-all.ru/3.4/library/select.html#module-select).431432Новое в версии 3.3.433434#### `terminate()`435436Завершает процесс. В Unix для этого используется сигнал `SIGTERM`; в Windows – `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.п. выполнены не будут.437438Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.439440> **Предупреждение**441>442> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.443444Обратите внимание, что методы [`start()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.is_alive), [`terminate()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.terminate) и [`exitcode`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.exitcode) должны вызываться только процессом, который создал объект процесса.445446Пример использования некоторых методов [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process):447448```python449>>> import multiprocessing, time, signal450>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))451>>> print(p, p.is_alive())452<Process(Process-1, initial)> False453>>> p.start()454>>> print(p, p.is_alive())455<Process(Process-1, started)> True456>>> p.terminate()457>>> time.sleep(0.1)458>>> print(p, p.is_alive())459<Process(Process-1, stopped[SIGTERM])> False460>>> p.exitcode == -signal.SIGTERM461True462```463464#### `exception multiprocessing.ProcessError`465466Базовый класс всех исключений [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing).467468#### `exception multiprocessing.BufferTooShort`469470Исключение, вызываемое [`Connection.recv_bytes_into()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.recv_bytes_into), когда переданный объект буфера слишком мал для читаемого сообщения.471472Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде строки байтов.473474#### `exception multiprocessing.AuthenticationError`475476Возбуждается при ошибке аутентификации.477478#### `exception multiprocessing.TimeoutError`479480Возбуждается методами с тайм-аутом по истечении тайм-аута.481482### 17.2.2.2. Каналы и очереди483484При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.485486Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает множество производителей и потребителей).487488Типы [`Queue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue), [`SimpleQueue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.SimpleQueue) и [`JoinableQueue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue) представляют собой очереди FIFO со многими производителями и потребителями, построенные по образцу класса [`queue.Queue`](https://python-all.ru/3.4/library/queue.html#queue.Queue) из стандартной библиотеки. Они отличаются тем, что [`Queue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue) не имеет методов [`task_done()`](https://python-all.ru/3.4/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.4/library/queue.html#queue.Queue.join), появившихся в классе [`queue.Queue`](https://python-all.ru/3.4/library/queue.html#queue.Queue) из Python 2.5.489490Если используется [`JoinableQueue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue), то **необходимо** вызывать [`JoinableQueue.task_done()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди, иначе семафор, используемый для подсчёта количества незавершённых задач, может в конечном счёте переполниться, что приведёт к возникновению исключения.491492Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [*Менеджеры*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-managers).493494> **Примечание**495>496> [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) использует стандартные исключения [`queue.Empty`](https://python-all.ru/3.4/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.4/library/queue.html#queue.Full) для сигнализации о тайм-ауте. Они недоступны в пространстве имён [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing), поэтому их необходимо импортировать из [`queue`](https://python-all.ru/3.4/library/queue.html#module-queue).497498> **Примечание**499>500> Когда объект помещается в очередь, он сериализуется с помощью модуля pickle, и фоновый поток позднее сбрасывает сериализованные данные в нижележащий канал. Это имеет некоторые неожиданные последствия, но обычно не вызывает практических трудностей – если это действительно беспокоит, можно вместо этого использовать очередь, созданную с помощью [*менеджера*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-managers).501>502> 1. После помещения объекта в пустую очередь может возникнуть бесконечно малая задержка, прежде чем метод [`empty()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.empty) очереди вернёт [`False`](https://python-all.ru/3.4/library/constants.html#False), и [`get_nowait()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.get_nowait) сможет вернуть значение, не вызывая [`queue.Empty`](https://python-all.ru/3.4/library/queue.html#queue.Empty).503> 2. Если несколько процессов помещают объекты в очередь, возможно получение объектов на другом конце не по порядку. Однако объекты, помещённые в очередь одним и тем же процессом, всегда будут идти друг за другом в ожидаемом порядке.504505> **Предупреждение**506>507> Если процесс завершается с помощью [`Process.terminate()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/3.4/library/os.html#os.kill) в то время, когда он пытается использовать [`Queue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue), данные в очереди скорее всего будут повреждены. Из-за этого любой другой процесс может получить исключение при попытке использовать очередь впоследствии.508509> **Предупреждение**510>511> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не вызывал [`JoinableQueue.cancel_join_thread`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread)), то этот процесс не завершится, пока все буферизованные элементы не будут сброшены в канал.512>513> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.514>515> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [*Рекомендации по программированию*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-programming).516517Пример использования очередей для межпроцессного взаимодействия см. в разделе [*Примеры*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-examples).518519#### `multiprocessing.Pipe([duplex])`520521Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection), представляющих концы канала.522523Если *duplex* равен `True` (по умолчанию), то канал двунаправленный. Если *duplex* равен `False`, то канал однонаправленный: `conn1` может использоваться только для получения сообщений, а `conn2` – только для отправки сообщений.524525#### `class multiprocessing.Queue([maxsize])`526527Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.528529Обычные исключения [`queue.Empty`](https://python-all.ru/3.4/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.4/library/queue.html#queue.Full) из модуля [`queue`](https://python-all.ru/3.4/library/queue.html#module-queue) стандартной библиотеки возбуждаются при тайм-ауте.530531[`Queue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue) реализует все методы [`queue.Queue`](https://python-all.ru/3.4/library/queue.html#queue.Queue), за исключением [`task_done()`](https://python-all.ru/3.4/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.4/library/queue.html#queue.Queue.join).532533#### `qsize()`534535Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.536537Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/3.4/library/exceptions.html#NotImplementedError) на платформах Unix, таких как Mac OS X, где `sem_getvalue()` не реализован.538539#### `empty()`540541Возвращает `True`, если очередь пуста, и `False` в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.542543#### `full()`544545Возвращает `True`, если очередь полна, и `False` в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.546547#### `put(obj[, block[, timeout]])`548549Помещает obj в очередь. Если необязательный аргумент *block* равен `True` (по умолчанию), а *timeout* равен `None` (по умолчанию), то блокируется при необходимости до появления свободного места. Если *timeout* – положительное число, то блокируется не более *timeout* секунд и вызывает исключение [`queue.Full`](https://python-all.ru/3.4/library/queue.html#queue.Full), если в течение этого времени свободное место не появилось. В противном случае (*block* равен `False`), помещает элемент в очередь, если свободное место доступно немедленно, иначе вызывает исключение [`queue.Full`](https://python-all.ru/3.4/library/queue.html#queue.Full) (*timeout* в этом случае игнорируется).550551#### `put_nowait(obj)`552553Эквивалентно `put(obj, False)`.554555#### `get([block[, timeout]])`556557Remove 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.4/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.4/library/queue.html#queue.Empty) exception (*timeout* is ignored in that case).558559#### `get_nowait()`560561Эквивалентно `get(False)`.562563У [`multiprocessing.Queue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue) есть несколько дополнительных методов, отсутствующих в [`queue.Queue`](https://python-all.ru/3.4/library/queue.html#queue.Queue). В большинстве кода эти методы обычно не нужны:564565#### `close()`566567Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.568569#### `join_thread()`570571Присоединяет фоновый поток. Используется только после вызова [`close()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.close). Блокирует выполнение до завершения фонового потока, гарантируя, что все данные из буфера будут сброшены в канал.572573По умолчанию, если процесс не является создателем очереди, то при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы [`join_thread()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.join_thread) не делала ничего.574575#### `cancel_join_thread()`576577Предотвращает блокирование [`join_thread()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.join_thread). В частности, это не даёт фоновому потоку автоматически присоединиться при завершении процесса – см. [`join_thread()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.join_thread).578579Более удачным названием для этого метода могло бы быть `allow_exit_without_flush()`. Его использование, скорее всего, приведёт к потере поставленных в очередь данных, и вам почти наверняка не понадобится его вызывать. Он действительно нужен только в том случае, если текущему процессу необходимо немедленно завершиться, не дожидаясь сброса данных из очереди в нижележащий канал, и вам всё равно, что данные будут потеряны.580581> **Примечание**582>583> Функциональность этого класса требует работающей реализации разделяемого семафора в операционной системе. Без неё функциональность этого класса будет отключена, и попытки создать экземпляр [`очереди`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue) приведут к [`ImportError`](https://python-all.ru/3.4/library/exceptions.html#ImportError). Дополнительную информацию см. в [issue 3770](https://python-all.ru/3.4/library/multiprocessing.html). То же самое относится ко всем специализированным типам очередей, перечисленным ниже.584585#### `class multiprocessing.SimpleQueue`586587Упрощённый тип [`очереди`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue), очень близкий к блокируемому [`Pipe`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Pipe).588589#### `empty()`590591Возвращает `True`, если очередь пуста, и `False` в противном случае.592593#### `get()`594595Извлекает и возвращает элемент из очереди.596597#### `put(item)`598599Помещает *item* в очередь.600601#### `class multiprocessing.JoinableQueue([maxsize])`602603[`JoinableQueue`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue), подкласс [`очереди`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно содержит методы [`task_done()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue.join).604605#### `task_done()`606607Указывает, что ранее поставленная задача выполнена. Используется потребителями очереди. Для каждого вызова [`get()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.get), которым была получена задача, последующий вызов [`task_done()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) сообщает очереди, что обработка задачи завершена.608609Если вызов [`join()`](https://python-all.ru/3.4/library/queue.html#queue.Queue.join) в данный момент блокирован, он возобновится, когда все элементы будут обработаны (то есть для каждого элемента, который был [`put()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.put) в очередь, был получен вызов [`task_done()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done)).610611Вызывает [`ValueError`](https://python-all.ru/3.4/library/exceptions.html#ValueError), если вызывается больше раз, чем было помещено элементов в очередь.612613#### `join()`614615Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.616617Счётчик незавершённых задач увеличивается каждый раз, когда элемент добавляется в очередь. Счётчик уменьшается, когда потребитель вызывает [`task_done()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы сообщить, что элемент получен и вся работа с ним завершена. Когда счётчик незавершённых задач падает до нуля, [`join()`](https://python-all.ru/3.4/library/queue.html#queue.Queue.join) разблокируется.618619### 17.2.2.3. Разное620621#### `multiprocessing.active_children()`622623Возвращает список всех активных дочерних процессов текущего процесса.624625Вызов этой функции имеет побочный эффект – «присоединение» всех уже завершённых процессов.626627#### `multiprocessing.cpu_count()`628629Возвращает количество процессоров в системе. Может вызвать [`NotImplementedError`](https://python-all.ru/3.4/library/exceptions.html#NotImplementedError).630631> **См. также**632>633> [`os.cpu_count()`](https://python-all.ru/3.4/library/os.html#os.cpu_count)634635#### `multiprocessing.current_process()`636637Возвращает объект [`процесса`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.638639Аналог [`threading.current_thread()`](https://python-all.ru/3.4/library/threading.html#threading.current_thread).640641#### `multiprocessing.freeze_support()`642643Добавлена поддержка для случая, когда программа, использующая [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing), была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)644645Эту функцию нужно вызывать сразу после строки `if __name__ == '__main__'` главного модуля. Например:646647```python648from multiprocessing import Process, freeze_support649650def f():651 print('hello world!')652653if __name__ == '__main__':654 freeze_support()655 Process(target=f).start()656```657658Если строку `freeze_support()` опустить, то при попытке запустить замороженный исполняемый файл будет вызвано [`RuntimeError`](https://python-all.ru/3.4/library/exceptions.html#RuntimeError).659660Если модуль запускается обычным образом интерпретатором Python, то [`freeze_support()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.freeze_support) не действует.661662#### `multiprocessing.get_all_start_methods()`663664Возвращает список поддерживаемых методов запуска, первый из которых является методом по умолчанию. Возможные методы запуска: `'fork'`, `'spawn'` и `'forkserver'`. В Windows доступен только `'spawn'`. В Unix всегда поддерживаются `'fork'` и `'spawn'`, причём `'fork'` используется по умолчанию.665666Новое в версии 3.4.667668#### `multiprocessing.get_context(method=None)`669670Возвращает объект контекста с теми же атрибутами, что и модуль [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing).671672Если *method* равен *None*, возвращается контекст по умолчанию. В противном случае *method* должен быть `'fork'`, `'spawn'`, `'forkserver'`. Если указанный метод запуска недоступен, возбуждается [`ValueError`](https://python-all.ru/3.4/library/exceptions.html#ValueError).673674Новое в версии 3.4.675676#### `multiprocessing.get_start_method(allow_none=False)`677678Возвращает имя метода запуска, используемого для запуска процессов.679680Если метод запуска не был фиксирован и *allow\_none* равно false, то метод запуска фиксируется на значение по умолчанию и возвращается его имя. Если метод запуска не был фиксирован и *allow\_none* равно true, то возвращается *None*.681682Возвращаемое значение может быть `'fork'`, `'spawn'`, `'forkserver'` или *None*. В Unix по умолчанию используется `'fork'`, а в Windows – `'spawn'`.683684Новое в версии 3.4.685686#### `multiprocessing.set_executable()`687688Задаёт путь к интерпретатору Python, который будет использоваться при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/3.4/library/sys.html#sys.executable)). Встраивающим Python разработчикам, вероятно, потребуется сделать что-то вроде689690```python691set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))692```693694прежде чем они смогут создавать дочерние процессы.695696Изменено в версии 3.4: Теперь поддерживается в Unix при использовании метода запуска `'spawn'`.697698#### `multiprocessing.set_start_method(method)`699700Задаёт метод, который должен использоваться для запуска дочерних процессов. *method* может быть `'fork'`, `'spawn'` или `'forkserver'`.701702Обратите внимание, что эту функцию следует вызывать не более одного раза, и она должна быть защищена внутри блока `if __name__ == '__main__'` главного модуля.703704Новое в версии 3.4.705706> **Примечание**707>708> [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) не содержит аналогов [`threading.active_count()`](https://python-all.ru/3.4/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/3.4/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/3.4/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/3.4/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/3.4/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/3.4/library/threading.html#threading.local).709710### 17.2.2.4. Объекты соединений711712Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.713714Объекты соединения обычно создаются с помощью [`Pipe()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Pipe) – см. также [*Слушатели и клиенты*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-listeners-clients).715716#### `class multiprocessing.Connection`717718#### `send(obj)`719720Отправляет объект на другой конец соединения; этот объект должен быть прочитан с помощью [`recv()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.recv).721722Объект должен быть сериализуем (picklable). Очень большие объекты (примерно от 32 МБ, хотя это зависит от ОС) могут вызвать исключение ValueError.723724#### `recv()`725726Возвращает объект, отправленный с другого конца соединения с помощью [`send()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.send). Блокируется, пока не появится что-то для приёма. Возбуждает [`EOFError`](https://python-all.ru/3.4/library/exceptions.html#EOFError), если больше нечего принимать и другой конец был закрыт.727728#### `fileno()`729730Возвращает файловый дескриптор или дескриптор (handle), используемый подключением.731732#### `close()`733734Закрывает подключение.735736Вызывается автоматически при сборке мусора для подключения.737738#### `poll([timeout])`739740Возвращает, есть ли данные для чтения.741742Если *timeout* не указан, то возврат происходит немедленно. Если *timeout* – число, оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, используется бесконечное ожидание.743744Обратите внимание, что несколько объектов соединения можно опрашивать одновременно с помощью [`multiprocessing.connection.wait()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.connection.wait).745746#### `send_bytes(buffer[, offset[, size]])`747748Отправляет байтовые данные из [*байтоподобного объекта*](https://python-all.ru/3.4/glossary.html#term-bytes-like-object) как полное сообщение.749750Если задан *offset*, то данные читаются с этой позиции в *buffer*. Если задан *size*, то из буфера будет прочитано соответствующее количество байт. Очень большие буферы (примерно от 32 МБ, хотя зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/3.4/library/exceptions.html#ValueError).751752#### `recv_bytes([maxlength])`753754Возвращает в виде строки полное байтовое сообщение, отправленное с другого конца соединения. Блокируется до появления данных для приёма. Вызывает [`EOFError`](https://python-all.ru/3.4/library/exceptions.html#EOFError), если больше нечего получать и другой конец закрыт.755756Если указан *maxlength* и сообщение длиннее *maxlength*, то вызывается [`OSError`](https://python-all.ru/3.4/library/exceptions.html#OSError), и соединение больше нельзя будет читать.757758Изменено в версии 3.3: Ранее эта функция вызывала [`IOError`](https://python-all.ru/3.4/library/exceptions.html#IOError), который теперь является псевдонимом [`OSError`](https://python-all.ru/3.4/library/exceptions.html#OSError).759760#### `recv_bytes_into(buffer[, offset])`761762Читает в *buffer* полное байтовое сообщение, отправленное с другого конца соединения, и возвращает количество байт в сообщении. Блокируется до появления данных для приёма. Вызывает [`EOFError`](https://python-all.ru/3.4/library/exceptions.html#EOFError), если больше нечего получать и другой конец закрыт.763764*buffer* должен быть доступным для записи [*байтоподобным объектом*](https://python-all.ru/3.4/glossary.html#term-bytes-like-object). Если задан *offset*, то сообщение будет записано в буфер, начиная с этой позиции. Offset должен быть неотрицательным целым числом, меньшим длины *buffer* (в байтах).765766Если буфер слишком мал, то вызывается исключение [`BufferTooShort`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.BufferTooShort), и полное сообщение доступно как `e.args[0]`, где `e` – это экземпляр исключения.767768Изменено в версии 3.3: Теперь сами объекты Connection можно передавать между процессами с помощью [`Connection.send()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.send) и [`Connection.recv()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.recv).769770Новое в версии 3.3: Объекты Connection теперь поддерживают протокол управления контекстом – см. [*Context Manager Types*](https://python-all.ru/3.4/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__enter__) возвращает объект соединения, а [`__exit__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__exit__) вызывает [`close()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.close).771772Например:773774```python775>>> from multiprocessing import Pipe776>>> a, b = Pipe()777>>> a.send([1, 'hello', None])778>>> b.recv()779[1, 'hello', None]780>>> b.send_bytes(b'thank you')781>>> a.recv_bytes()782b'thank you'783>>> import array784>>> arr1 = array.array('i', range(5))785>>> arr2 = array.array('i', [0] * 10)786>>> a.send_bytes(arr1)787>>> count = b.recv_bytes_into(arr2)788>>> assert count == len(arr1) * arr1.itemsize789>>> arr2790array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])791```792793> **Предупреждение**794>795> Метод [`Connection.recv()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.recv) автоматически десериализует полученные данные, что может представлять угрозу безопасности, если нет доверия к процессу, отправившему сообщение.796>797> Поэтому, если объект соединения не был создан с помощью [`Pipe()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Pipe), методы [`recv()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.recv) и [`send()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.send) следует использовать только после выполнения некоторой аутентификации. См. [*Authentication keys*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-auth-keys).798799> **Предупреждение**800>801> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.802803### 17.2.2.5. Примитивы синхронизации804805Обычно примитивы синхронизации не так необходимы в многопроцессной программе, как в многопоточной. См. документацию модуля [`threading`](https://python-all.ru/3.4/library/threading.html#module-threading).806807Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [*Менеджеры*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-managers).808809#### `class multiprocessing.Barrier(parties[, action[, timeout]])`810811Объект барьера: клон [`threading.Barrier`](https://python-all.ru/3.4/library/threading.html#threading.Barrier).812813Новое в версии 3.3.814815#### `class multiprocessing.BoundedSemaphore([value])`816817Объект ограниченного семафора: близкий аналог [`threading.BoundedSemaphore`](https://python-all.ru/3.4/library/threading.html#threading.BoundedSemaphore).818819Единственное отличие от его близкого аналога: первый аргумент метода `acquire` называется *block*, что согласуется с [`Lock.acquire()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock.acquire).820821> **Примечание**822>823> На Mac OS X это неотличимо от [`Semaphore`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализована на этой платформе.824825#### `class multiprocessing.Condition([lock])`826827Условная переменная: псевдоним для [`threading.Condition`](https://python-all.ru/3.4/library/threading.html#threading.Condition).828829Если указана *блокировка*, то это должен быть объект [`Lock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock) из [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing).830831Изменено в версии 3.3: Был добавлен метод [`wait_for()`](https://python-all.ru/3.4/library/threading.html#threading.Condition.wait_for).832833#### `class multiprocessing.Event`834835Клон [`threading.Event`](https://python-all.ru/3.4/library/threading.html#threading.Event).836837#### `class multiprocessing.Lock`838839Объект нерекурсивной блокировки: близкий аналог [`threading.Lock`](https://python-all.ru/3.4/library/threading.html#threading.Lock). Как только процесс или поток захватил блокировку, последующие попытки захватить её из любого процесса или потока будут блокироваться до её освобождения; любой процесс или поток может её освободить. Концепции и поведение [`threading.Lock`](https://python-all.ru/3.4/library/threading.html#threading.Lock) в применении к потокам воспроизводятся здесь в [`multiprocessing.Lock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock) в применении либо к процессам, либо к потокам, за исключением отмеченных случаев.840841Обратите внимание, что [`Lock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.Lock`, инициализированный контекстом по умолчанию.842843[`Lock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock) поддерживает протокол [*контекстного менеджера*](https://python-all.ru/3.4/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/3.4/reference/compound_stmts.html#with).844845#### `acquire(block=True, timeout=None)`846847Захватывает блокировку, блокирующую или неблокирующую.848849Если аргумент *block* установлен в `True` (по умолчанию), вызов метода будет блокироваться до тех пор, пока блокировка не будет в разблокированном состоянии, затем установит её в заблокированное и вернёт `True`. Обратите внимание, что имя этого первого аргумента отличается от такового в [`threading.Lock.acquire()`](https://python-all.ru/3.4/library/threading.html#threading.Lock.acquire).850851Если аргумент *block* установлен в `False`, вызов метода не блокируется. Если блокировка в данный момент находится в заблокированном состоянии, возвращается `False`; в противном случае блокировка устанавливается в заблокированное состояние и возвращается `True`.852853При вызове с положительным значением с плавающей точкой для *timeout* выполнение блокируется не дольше числа секунд, заданного *timeout*, если блокировку не удаётся захватить. Вызовы с отрицательным значением *timeout* равносильны *timeout*, равному нулю. Вызовы со значением *timeout* равным `None` (по умолчанию) устанавливают бесконечный период ожидания. Обратите внимание, что обработка отрицательных значений или `None` для *timeout* отличается от реализованного поведения в [`threading.Lock.acquire()`](https://python-all.ru/3.4/library/threading.html#threading.Lock.acquire). Аргумент *timeout* не имеет практического значения, если аргумент *block* установлен в `False`, и поэтому игнорируется. Возвращает `True`, если блокировка захвачена, или `False`, если период ожидания истёк.854855#### `release()`856857Освобождает блокировку. Может вызываться из любого процесса или потока, а не только из того, который изначально захватил блокировку.858859Поведение такое же, как в [`threading.Lock.release()`](https://python-all.ru/3.4/library/threading.html#threading.Lock.release), за исключением того, что при вызове на незахваченной блокировке возбуждается [`ValueError`](https://python-all.ru/3.4/library/exceptions.html#ValueError).860861#### `class multiprocessing.RLock`862863Объект рекурсивной блокировки: близкий аналог [`threading.RLock`](https://python-all.ru/3.4/library/threading.html#threading.RLock). Рекурсивная блокировка должна быть освобождена процессом или потоком, который её захватил. После захвата рекурсивной блокировки тот же процесс или поток может захватить её снова без блокировки; этот процесс или поток должен освободить её по одному разу за каждый захват.864865Обратите внимание, что [`RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.RLock`, инициализированный с контекстом по умолчанию.866867[`RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock) поддерживает протокол [*менеджера контекста*](https://python-all.ru/3.4/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/3.4/reference/compound_stmts.html#with).868869#### `acquire(block=True, timeout=None)`870871Захватывает блокировку, блокирующую или неблокирующую.872873При вызове с аргументом *block*, установленным в `True`, ожидать, пока блокировка не окажется в разблокированном состоянии (не принадлежащем ни одному процессу или потоку), если только блокировка уже не принадлежит текущему процессу или потоку. Затем текущий процесс или поток получает владение блокировкой (если он ещё не владеет ею), и уровень рекурсии внутри блокировки увеличивается на единицу, в результате чего возвращается значение `True`. Обратите внимание, что поведение этого первого аргумента имеет несколько отличий от реализации [`threading.RLock.acquire()`](https://python-all.ru/3.4/library/threading.html#threading.RLock.acquire), начиная с названия самого аргумента.874875При вызове с аргументом *block*, установленным в `False`, не блокировать выполнение. Если блокировка уже захвачена (и, следовательно, принадлежит) другому процессу или потоку, текущий процесс или поток не получает владения, и уровень рекурсии внутри блокировки не изменяется, в результате чего возвращается значение `False`. Если блокировка находится в разблокированном состоянии, текущий процесс или поток получает владение, и уровень рекурсии увеличивается, в результате чего возвращается значение `True`.876877Использование и поведение аргумента *timeout* такие же, как в [`Lock.acquire()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock.acquire). Обратите внимание, что некоторые особенности поведения *timeout* отличаются от реализованных в [`threading.RLock.acquire()`](https://python-all.ru/3.4/library/threading.html#threading.RLock.acquire).878879#### `release()`880881Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения уровень рекурсии становится нулевым, переводит блокировку в разблокированное состояние (не принадлежит ни одному процессу или потоку), и если какие-либо другие процессы или потоки ожидают разблокировки, позволяет ровно одному из них продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся заблокированной и принадлежащей вызывающему процессу или потоку.882883Вызывайте этот метод только тогда, когда вызывающий процесс или поток владеет блокировкой. Исключение [`AssertionError`](https://python-all.ru/3.4/library/exceptions.html#AssertionError) возбуждается, если этот метод вызывается процессом или потоком, не являющимся владельцем, или если блокировка находится в разблокированном (непринадлежащем) состоянии. Обратите внимание, что тип возбуждаемого в этой ситуации исключения отличается от реализованного в [`threading.RLock.release()`](https://python-all.ru/3.4/library/threading.html#threading.RLock.release).884885#### `class multiprocessing.Semaphore([value])`886887Объект семафора: близкий аналог [`threading.Semaphore`](https://python-all.ru/3.4/library/threading.html#threading.Semaphore).888889Существует единственное отличие от его ближайшего аналога: первый аргумент его метода `acquire` называется *block*, что согласуется с [`Lock.acquire()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock.acquire).890891> **Примечание**892>893> В Mac OS X функция `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с таймаутом будет эмулировать её поведение с помощью цикла ожидания.894895> **Примечание**896>897> Если сигнал SIGINT, генерируемый `Ctrl-C`, поступает, пока главный поток заблокирован вызовом `BoundedSemaphore.acquire()`, [`Lock.acquire()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock.acquire), [`RLock.acquire()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock.acquire), `Semaphore.acquire()`, `Condition.acquire()` или `Condition.wait()`, то вызов будет немедленно прерван, и будет возбуждено исключение [`KeyboardInterrupt`](https://python-all.ru/3.4/library/exceptions.html#KeyboardInterrupt).898>899> Это отличается от поведения [`threading`](https://python-all.ru/3.4/library/threading.html#module-threading), где SIGINT будет игнорироваться, пока выполняются эквивалентные блокирующие вызовы.900901> **Примечание**902>903> Часть функционала этого пакета требует работающей реализации разделяемого семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки его импорта приведут к [`ImportError`](https://python-all.ru/3.4/library/exceptions.html#ImportError). Дополнительную информацию см. в [issue 3770](https://python-all.ru/3.4/library/multiprocessing.html).904905### 17.2.2.6. Разделяемые [`ctypes`](https://python-all.ru/3.4/library/ctypes.html#module-ctypes) объекты906907Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.908909#### `multiprocessing.Value(typecode_or_type, *args, lock=True)`910911Возвращает объект [`ctypes`](https://python-all.ru/3.4/library/ctypes.html#module-ctypes), выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для объекта. Доступ к самому объекту можно получить через атрибут *value* объекта [`Value`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Value).912913*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.4/library/array.html#module-array). *\*args* передаётся конструктору типа.914915Если *lock* равен `True` (по умолчанию), то создаётся новый объект рекурсивной блокировки для синхронизации доступа к значению. Если *lock* является объектом [`Lock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *lock* равен `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».916917Операции, подобные `+=`, которые включают чтение и запись, не являются атомарными. Поэтому, если, например, требуется атомарно увеличить разделяемое значение, недостаточно просто сделать918919```python920counter.value += 1921```922923При условии, что связанная блокировка рекурсивна (по умолчанию она такой и является), можно вместо этого сделать924925```python926with counter.get_lock():927 counter.value += 1928```929930Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.931932#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`933934Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.935936*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.4/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* является последовательностью, которая используется для инициализации массива, и её длина определяет длину массива.937938Если *lock* равен `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *lock* является объектом [`Lock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *lock* равен `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».939940Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.941942Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.4/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.943944#### 17.2.2.6.1. Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing.sharedctypes)945946Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing.sharedctypes) предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/3.4/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.947948> **Примечание**949>950> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.951952#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`953954Возвращает массив ctypes, выделенный из разделяемой памяти.955956*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.4/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно задаёт длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.957958Обратите внимание, что запись и чтение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.959960#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`961962Возвращает объект ctypes, выделенный из разделяемой памяти.963964*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.4/library/array.html#module-array). *\*args* передаётся конструктору типа.965966Обратите внимание, что запись и чтение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.967968Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.4/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют хранить и извлекать строки – см. документацию по [`ctypes`](https://python-all.ru/3.4/library/ctypes.html#module-ctypes).969970#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *, lock=True)`971972То же, что и [`RawArray()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного массива ctypes.973974Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».975976Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.977978#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args, lock=True)`979980То же, что и [`RawValue()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного объекта ctypes.981982Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».983984Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.985986#### `multiprocessing.sharedctypes.copy(obj)`987988Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes *obj*.989990#### `multiprocessing.sharedctypes.synchronized(obj[, lock])`991992Возвращает процесс-безопасный объект-обёртку для объекта ctypes, который использует *блокировка* для синхронизации доступа. Если *блокировка* равно `None` (по умолчанию), то автоматически создаётся объект [`multiprocessing.RLock`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.RLock).993994Синхронизированная обёртка будет иметь два дополнительных метода по сравнению с объектом, который она оборачивает: `get_obj()` возвращает обёрнутый объект, а `get_lock()` возвращает объект блокировки, используемый для синхронизации.995996Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.997998В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некий подкласс [`ctypes.Structure`](https://python-all.ru/3.4/library/ctypes.html#ctypes.Structure).)9991000| ctypes | sharedctypes с типом | sharedctypes с typecode |1001| --- | --- | --- |1002| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |1003| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) | |1004| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |1005| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |10061007Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:10081009```python1010from multiprocessing import Process, Lock1011from multiprocessing.sharedctypes import Value, Array1012from ctypes import Structure, c_double10131014class Point(Structure):1015 _fields_ = [('x', c_double), ('y', c_double)]10161017def modify(n, x, s, A):1018 n.value **= 21019 x.value **= 21020 s.value = s.value.upper()1021 for a in A:1022 a.x **= 21023 a.y **= 210241025if __name__ == '__main__':1026 lock = Lock()10271028 n = Value('i', 7)1029 x = Value(c_double, 1.0/3.0, lock=False)1030 s = Array('c', b'hello world', lock=lock)1031 A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)10321033 p = Process(target=modify, args=(n, x, s, A))1034 p.start()1035 p.join()10361037 print(n.value)1038 print(x.value)1039 print(s.value)1040 print([(a.x, a.y) for a in A])1041```10421043Выводятся следующие результаты:10441045```text10464910470.11111111111111111048HELLO WORLD1049[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]1050```10511052### 17.2.2.7. Менеджеры10531054Менеджеры предоставляют способ создания данных, которыми можно обмениваться между разными процессами, в том числе по сети между процессами, выполняющимися на разных машинах. Объект менеджера управляет серверным процессом, который управляет *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.10551056#### `multiprocessing.Manager()`10571058Возвращает запущенный объект [`SyncManager`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.SyncManager), который можно использовать для обмена объектами между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.10591060Процессы менеджеров завершаются, как только они собираются сборщиком мусора или их родительский процесс завершается. Классы менеджеров определены в модуле [`multiprocessing.managers`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing.managers):10611062#### `class multiprocessing.managers.BaseManager([address[, authkey]])`10631064Создает объект BaseManager.10651066После создания следует вызвать [`start()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или `get_server().serve_forever()`, чтобы гарантировать, что объект менеджера ссылается на запущенный процесс менеджера.10671068*address* – это адрес, по которому процесс менеджера ожидает новые подключения. Если *address* равен `None`, то выбирается произвольный.10691070*authkey* – это ключ аутентификации, который будет использоваться для проверки подлинности входящих подключений к процессу сервера. Если *authkey* равен `None`, то используется `current_process().authkey`. В противном случае используется *authkey*, и он должен быть строкой байтов.10711072#### `start([initializer[, initargs]])`10731074Запускает подпроцесс для запуска менеджера. Если *initializer* не равен `None`, то подпроцесс вызовет `initializer(*initargs)` при запуске.10751076#### `get_server()`10771078Возвращает объект `Server`, представляющий реальный сервер под управлением Manager. Объект `Server` поддерживает метод `serve_forever()`:10791080```python1081>>> from multiprocessing.managers import BaseManager1082>>> manager = BaseManager(address=('', 50000), authkey=b'abc')1083>>> server = manager.get_server()1084>>> server.serve_forever()1085```10861087`Server` также имеет атрибут [`address`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).10881089#### `connect()`10901091Подключает локальный объект менеджера к удаленному процессу менеджера:10921093```python1094>>> from multiprocessing.managers import BaseManager1095>>> m = BaseManager(address=('127.0.0.1', 5000), authkey=b'abc')1096>>> m.connect()1097```10981099#### `shutdown()`11001101Останавливает процесс, используемый менеджером. Это доступно только в том случае, если [`start()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.11021103Этот метод можно вызывать несколько раз.11041105#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`11061107Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.11081109*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.11101111*callable* – это вызываемый объект, используемый для создания объектов для данного идентификатора типа. Если экземпляр менеджера будет подключен к серверу с помощью метода [`connect()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager.connect) или если аргумент *create\_method* равен `False`, то этот параметр можно оставить равным `None`.11121113*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.11141115*exposed* используется для указания последовательности имен методов, к которым прокси для данного typeid должны иметь доступ с помощью [`BaseProxy._callmethod()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). (Если *exposed* равен `None`, то вместо него используется `proxytype._exposed_`, если он существует.) В случае, когда список exposed не указан, все «публичные методы» разделяемого объекта будут доступны. (Здесь «публичный метод» означает любой атрибут, у которого есть метод [`__call__()`](https://python-all.ru/3.4/reference/datamodel.html#object.__call__) и имя которого не начинается с `'_'`.)11161117*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно сопоставляет имена методов со строками typeid. (Если *method\_to\_typeid* равен `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом в этом отображении или если отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.11181119*create\_method* определяет, должен ли быть создан метод с именем *typeid*, который можно использовать, чтобы сообщить серверному процессу о создании нового разделяемого объекта и возврате прокси для него. По умолчанию это `True`.11201121Экземпляры [`BaseManager`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:11221123#### `address`11241125Адрес, используемый менеджером.11261127Изменено в версии 3.3: Объекты Manager поддерживают протокол управления контекстом – см. [*Context Manager Types*](https://python-all.ru/3.4/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__enter__) запускает серверный процесс (если он ещё не запущен) и затем возвращает объект менеджера. [`__exit__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__exit__) вызывает [`shutdown()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager.shutdown).11281129В предыдущих версиях [`__enter__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__enter__) не запускал серверный процесс менеджера, если он ещё не был запущен.11301131#### `class multiprocessing.managers.SyncManager`11321133Подкласс [`BaseManager`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.11341135Он также поддерживает создание разделяемых списков и словарей.11361137#### `Barrier(parties[, action[, timeout]])`11381139Создаёт разделяемый объект [`threading.Barrier`](https://python-all.ru/3.4/library/threading.html#threading.Barrier) и возвращает прокси для него.11401141Новое в версии 3.3.11421143#### `BoundedSemaphore([value])`11441145Создаёт разделяемый объект [`threading.BoundedSemaphore`](https://python-all.ru/3.4/library/threading.html#threading.BoundedSemaphore) и возвращает прокси для него.11461147#### `Condition([lock])`11481149Создаёт разделяемый объект [`threading.Condition`](https://python-all.ru/3.4/library/threading.html#threading.Condition) и возвращает прокси для него.11501151Если *блокировка* указан, то он должен быть прокси для объекта [`threading.Lock`](https://python-all.ru/3.4/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/3.4/library/threading.html#threading.RLock).11521153Изменено в версии 3.3: Был добавлен метод [`wait_for()`](https://python-all.ru/3.4/library/threading.html#threading.Condition.wait_for).11541155#### `Event()`11561157Создаёт разделяемый объект [`threading.Event`](https://python-all.ru/3.4/library/threading.html#threading.Event) и возвращает для него прокси.11581159#### `Lock()`11601161Создаёт разделяемый объект [`threading.Lock`](https://python-all.ru/3.4/library/threading.html#threading.Lock) и возвращает для него прокси.11621163#### `Namespace()`11641165Создаёт разделяемый объект [`Namespace`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.SyncManager.Namespace) и возвращает для него прокси.11661167#### `Queue([maxsize])`11681169Создаёт разделяемый объект [`queue.Queue`](https://python-all.ru/3.4/library/queue.html#queue.Queue) и возвращает для него прокси.11701171#### `RLock()`11721173Создаёт разделяемый объект [`threading.RLock`](https://python-all.ru/3.4/library/threading.html#threading.RLock) и возвращает для него прокси.11741175#### `Semaphore([value])`11761177Создаёт разделяемый объект [`threading.Semaphore`](https://python-all.ru/3.4/library/threading.html#threading.Semaphore) и возвращает прокси для него.11781179#### `Array(typecode, sequence)`11801181Создать массив и вернуть для него прокси.11821183#### `Value(typecode, value)`11841185Создаёт объект с атрибутом `value`, доступным для записи, и возвращает прокси для него.11861187#### `dict()`11881189#### `dict(mapping)`11901191#### `dict(sequence)`11921193Создаёт разделяемый объект `dict` и возвращает для него прокси.11941195#### `list()`11961197#### `list(sequence)`11981199Создаёт разделяемый объект `list` и возвращает для него прокси.12001201> **Примечание**1202>1203> Изменения изменяемых значений или элементов в прокси-объектах словарей и списков не будут распространяться через менеджер, поскольку прокси не может отследить, когда его значения или элементы изменяются. Чтобы изменить такой элемент, можно переназначить изменённый объект обратно в прокси-контейнер:1204>1205> ```python1206> # создать прокси для списка и добавить изменяемый объект (словарь)1207> lproxy = manager.list()1208> lproxy.append({})1209> # теперь изменить словарь1210> d = lproxy[0]1211> d['a'] = 11212> d['b'] = 21213> # на данный момент изменения в d еще не синхронизированы, но при1214> # при переприсваивании словаря прокси получает уведомление об изменении1215> lproxy[0] = d1216> ```12171218#### 17.2.2.7.1. Namespace-объекты12191220Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.12211222Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:12231224```python1225>>> manager = multiprocessing.Manager()1226>>> Global = manager.Namespace()1227>>> Global.x = 101228>>> Global.y = 'hello'1229>>> Global._z = 12.3 # это атрибут прокси1230>>> print(Global)1231Namespace(x=10, y='hello')1232```12331234#### 17.2.2.7.2. Настраиваемые менеджеры12351236Чтобы создать собственный менеджер, создаётся подкласс [`BaseManager`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager) и используется метод класса [`register()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:12371238```python1239from multiprocessing.managers import BaseManager12401241class MathsClass:1242 def add(self, x, y):1243 return x + y1244 def mul(self, x, y):1245 return x * y12461247class MyManager(BaseManager):1248 pass12491250MyManager.register('Maths', MathsClass)12511252if __name__ == '__main__':1253 with MyManager() as manager:1254 maths = manager.Maths()1255 print(maths.add(4, 3)) # выводит 71256 print(maths.mul(7, 8)) # выводит 561257```12581259#### 17.2.2.7.3. Использование удалённого менеджера12601261Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).12621263Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:12641265```python1266>>> from multiprocessing.managers import BaseManager1267>>> import queue1268>>> queue = queue.Queue()1269>>> class QueueManager(BaseManager): pass1270>>> QueueManager.register('get_queue', callable=lambda:queue)1271>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1272>>> s = m.get_server()1273>>> s.serve_forever()1274```12751276Один клиент может получить доступ к серверу следующим образом:12771278```python1279>>> from multiprocessing.managers import BaseManager1280>>> class QueueManager(BaseManager): pass1281>>> QueueManager.register('get_queue')1282>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1283>>> m.connect()1284>>> queue = m.get_queue()1285>>> queue.put('hello')1286```12871288Другой клиент также может использовать его:12891290```python1291>>> from multiprocessing.managers import BaseManager1292>>> class QueueManager(BaseManager): pass1293>>> QueueManager.register('get_queue')1294>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1295>>> m.connect()1296>>> queue = m.get_queue()1297>>> queue.get()1298'hello'1299```13001301Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:13021303```python1304>>> from multiprocessing import Process, Queue1305>>> from multiprocessing.managers import BaseManager1306>>> class Worker(Process):1307... def __init__(self, q):1308... self.q = q1309... super(Worker, self).__init__()1310... def run(self):1311... self.q.put('local hello')1312...1313>>> queue = Queue()1314>>> w = Worker(queue)1315>>> w.start()1316>>> class QueueManager(BaseManager): pass1317...1318>>> QueueManager.register('get_queue', callable=lambda: queue)1319>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1320>>> s = m.get_server()1321>>> s.serve_forever()1322```13231324### 17.2.2.8. Прокси-объекты13251326Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.13271328Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:13291330```python1331>>> from multiprocessing import Manager1332>>> manager = Manager()1333>>> l = manager.list([i*i for i in range(10)])1334>>> print(l)1335[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1336>>> print(repr(l))1337<ListProxy object, typeid 'list' at 0x...>1338>>> l[4]1339161340>>> l[2:5]1341[4, 9, 16]1342```13431344Обратите внимание, что применение [`str()`](https://python-all.ru/3.4/library/stdtypes.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/3.4/library/functions.html#repr) вернёт представление прокси.13451346Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:13471348```python1349>>> a = manager.list()1350>>> b = manager.list()1351>>> a.append(b) # референт a теперь содержит референт b1352>>> print(a, b)1353[[]] []1354>>> b.append('hello')1355>>> print(a, b)1356[['hello']] ['hello']1357```13581359> **Примечание**1360>1361> Типы прокси в [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) не поддерживают сравнение по значению. Так, например, имеем:1362>1363> ```python1364> >>> manager.list([1,2,3]) == [1,2,3]1365> False1366> ```1367>1368> При сравнении следует просто использовать копию референта.13691370#### `class multiprocessing.managers.BaseProxy`13711372Объекты прокси являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseProxy).13731374#### `_callmethod(methodname[, args[, kwds]])`13751376Вызывает метод референта прокси и возвращает результат.13771378Если `proxy` является прокси, чей референт – `obj`, то выражение13791380```python1381proxy._callmethod(methodname, args, kwds)1382```13831384вычислит выражение13851386```python1387getattr(obj, methodname)(*args, **kwds)1388```13891390в процессе менеджера.13911392Возвращаемое значение будет копией результата вызова или прокси на новый разделяемый объект – см. документацию по аргументу *method\_to\_typeid* метода [`BaseManager.register()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).13931394Если вызов порождает исключение, оно возбуждается повторно в [`_callmethod()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). Если в процессе менеджера возникло другое исключение, оно преобразуется в исключение `RemoteError` и возбуждается в [`_callmethod()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod).13951396Обратите внимание, что исключение будет возбуждаться, если *methodname* не был *экспонирован*.13971398Пример использования [`_callmethod()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):13991400```python1401>>> l = manager.list(range(10))1402>>> l._callmethod('__len__')1403101404>>> l._callmethod('__getitem__', (slice(2, 7),)) # эквивалентно `l[2:7]`1405[2, 3, 4, 5, 6]1406>>> l._callmethod('__getitem__', (20,)) # эквивалентно `l[20]`1407Traceback (most recent call last):1408...1409IndexError: list index out of range1410```14111412#### `_getvalue()`14131414Возвращает копию референта.14151416Если референт не может быть сериализован (unpicklable), то это вызовет исключение.14171418#### `__repr__()`14191420Возвращает представление прокси-объекта.14211422#### `__str__()`14231424Возвращает представление референта.14251426#### 17.2.2.8.1. Очистка14271428Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.14291430Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.14311432### 17.2.2.9. Пулы процессов14331434Можно создать пул процессов, которые будут выполнять переданные ему задачи с помощью класса [`Pool`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool).14351436#### `class multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])`14371438Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.14391440*processes* – количество рабочих процессов. Если *processes* равно `None`, то используется число, возвращаемое [`os.cpu_count()`](https://python-all.ru/3.4/library/os.html#os.cpu_count).14411442Если *initializer* не равен `None`, то каждый рабочий процесс при запуске вызовет `initializer(*initargs)`.14431444*maxtasksperchild* – количество задач, которое может выполнить рабочий процесс до завершения и замены новым процессом, чтобы освободить неиспользуемые ресурсы. Значение по умолчанию *maxtasksperchild* равно None, поэтому рабочие процессы живут столько же, сколько и пул.14451446*context* используется для указания контекста запуска рабочих процессов. Обычно пул создаётся с помощью функции `multiprocessing.Pool()` или метода [`Pool()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool) объекта контекста. В обоих случаях *context* устанавливается соответствующим образом.14471448Обратите внимание, что методы объекта пула должны вызываться только тем процессом, который создал пул.14491450Новое в версии 3.2: *maxtasksperchild*14511452Новое в версии 3.4: *context*14531454> **Примечание**1455>1456> Рабочие процессы внутри [`Pool`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool) обычно живут всё время существования очереди задач пула. Частая практика в других системах (например, Apache, mod\_wsgi и т.д.) для освобождения ресурсов, занятых рабочими процессами, – разрешить процессу выполнить только определённое количество работы, после чего он завершается, очищается и заменяется новым процессом. Аргумент *maxtasksperchild* конструктора [`Pool`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool) предоставляет эту возможность конечному пользователю.14571458#### `apply(func[, args[, kwds]])`14591460Вызывает *func* с аргументами *args* и именованными аргументами *kwds*. Блокирует выполнение до получения результата. Поскольку этот метод блокирует, [`apply_async()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.apply_async) лучше подходит для параллельной работы. Кроме того, *func* выполняется только в одном из рабочих процессов пула.14611462#### `apply_async(func[, args[, kwds[, callback[, error_callback]]]])`14631464Вариант метода [`apply()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.apply), который возвращает объект результата.14651466Если указан *колбэк*, то это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк*, если только вызов не завершился ошибкой – в этом случае вместо него применяется *колбэк ошибки*.14671468Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.14691470Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.14711472#### `map(func, iterable[, chunksize])`14731474Параллельный аналог встроенной функции [`map()`](https://python-all.ru/3.4/library/functions.html#map) (однако поддерживает только один аргумент *iterable*). Блокирует выполнение до получения результата.14751476Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.14771478#### `map_async(func, iterable[, chunksize[, callback[, error_callback]]])`14791480Вариант метода [`map()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.map), который возвращает объект результата.14811482Если указан *колбэк*, то это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк*, если только вызов не завершился ошибкой – в этом случае вместо него применяется *колбэк ошибки*.14831484Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.14851486Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.14871488#### `imap(func, iterable[, chunksize])`14891490Более ленивая версия [`map()`](https://python-all.ru/3.4/library/functions.html#map).14911492The *chunksize* argument is the same as the one used by the [`map()`](https://python-all.ru/3.4/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`.14931494Кроме того, если *chunksize* равен `1`, то метод `next()` итератора, возвращаемого методом [`imap()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` возбудит [`multiprocessing.TimeoutError`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.TimeoutError), если результат не будет получен в течение *timeout* секунд.14951496#### `imap_unordered(func, iterable[, chunksize])`14971498То же, что и [`imap()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.imap), но порядок результатов из возвращаемого итератора может быть произвольным. (Только при наличии одного рабочего процесса порядок гарантированно «правильный».)14991500#### `starmap(func, iterable[, chunksize])`15011502Как [`map()`](https://python-all.ru/3.4/library/functions.html#map), но элементы *iterable* должны быть итерируемыми объектами, которые распаковываются как аргументы.15031504Следовательно, *iterable* из `[(1,2), (3, 4)]` даёт `[func(1,2), func(3,4)]`.15051506Новое в версии 3.3.15071508#### `starmap_async(func, iterable[, chunksize[, callback[, error_back]]])`15091510Комбинация [`starmap()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.starmap) и [`map_async()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.map_async), которая перебирает *iterable* – итерируемый объект, содержащий итерируемые объекты – и вызывает *func* с распакованными итерируемыми объектами. Возвращает объект-результат.15111512Новое в версии 3.3.15131514#### `close()`15151516Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.15171518#### `terminate()`15191520Немедленно останавливает рабочие процессы, не завершая незавершённую работу. Когда объект пула собирается сборщиком мусора, [`terminate()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.terminate) будет вызван немедленно.15211522#### `join()`15231524Ожидает завершения рабочих процессов. Перед вызовом [`join()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.join) необходимо вызвать [`close()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.close) или [`terminate()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.terminate).15251526Новое в версии 3.3: Объекты Pool теперь поддерживают протокол управления контекстом – см. [*Context Manager Types*](https://python-all.ru/3.4/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__enter__) возвращает объект пула, а [`__exit__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__exit__) вызывает [`terminate()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.terminate).15271528#### `class multiprocessing.pool.AsyncResult`15291530Класс результата, возвращаемого [`Pool.apply_async()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.apply_async) и [`Pool.map_async()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool.map_async).15311532#### `get([timeout])`15331534Возвращает результат по его получении. Если *timeout* не равен `None` и результат не получен в течение *timeout* секунд, то возбуждается [`multiprocessing.TimeoutError`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.TimeoutError). Если удалённый вызов породил исключение, то оно будет возбуждено повторно вызовом [`get()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get).15351536#### `wait([timeout])`15371538Ожидает, пока результат не станет доступен или пока не пройдёт *timeout* секунд.15391540#### `ready()`15411542Возвращает, завершён ли вызов.15431544#### `successful()`15451546Возвращает, завершился ли вызов без возбуждения исключения. Возбуждает [`AssertionError`](https://python-all.ru/3.4/library/exceptions.html#AssertionError), если результат ещё не готов.15471548Следующий пример демонстрирует использование пула:15491550```python1551from multiprocessing import Pool15521553def f(x):1554 return x*x15551556if __name__ == '__main__':1557 with Pool(processes=4) as pool: # запустить 4 рабочих процесса1558 result = pool.apply_async(f, (10,)) # асинхронно вычислить "f(10)"1559 print(result.get(timeout=1)) # выводит "100", если компьютер не *очень* медленный15601561 print(pool.map(f, range(10))) # выводит "[0, 1, 4,..., 81]"15621563 it = pool.imap(f, range(10))1564 print(next(it)) # выводит "0"1565 print(next(it)) # выводит "1"1566 print(it.next(timeout=1)) # выводит "4", если компьютер не *очень* медленный15671568 import time1569 result = pool.apply_async(time.sleep, (10,))1570 print(result.get(timeout=1)) # вызывает TimeoutError1571```15721573### 17.2.2.10. Слушатели и клиенты15741575Обычно передача сообщений между процессами осуществляется через очереди или с помощью объектов [`Connection`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection), возвращаемых [`Pipe()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Pipe).15761577Однако модуль [`multiprocessing.connection`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing.connection) обеспечивает дополнительную гибкость. По сути, он предоставляет высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows. Он также поддерживает *дайджест-аутентификацию* с помощью модуля [`hmac`](https://python-all.ru/3.4/library/hmac.html#module-hmac) и возможность опроса нескольких соединений одновременно.15781579#### `multiprocessing.connection.deliver_challenge(connection, authkey)`15801581Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.15821583Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.AuthenticationError).15841585#### `multiprocessing.connection.answer_challenge(connection, authkey)`15861587Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.15881589Если приветственное сообщение не получено, то возбуждается [`AuthenticationError`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.AuthenticationError).15901591#### `multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`15921593Пытается установить соединение с прослушивателем, который использует адрес *address*, и возвращает объект [`Connection`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection).15941595Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [*Address Formats*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-address-formats))15961597Если *authenticate* равен `True` или *authkey* является байтовой строкой, то используется дайджест-аутентификация. Ключом для аутентификации будет либо *authkey*, либо `current_process().authkey`, если *authkey* равен `None`. Если аутентификация не удалась, то возбуждается [`AuthenticationError`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.AuthenticationError). См. [*Authentication keys*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-auth-keys).15981599#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`16001601Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.16021603*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.16041605> **Примечание**1606>1607> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.16081609*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них только первый гарантированно доступен. Если *family* равен `None`, то семейство определяется по формату *address*. Если *address* также равен `None`, то выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [*Address Formats*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равен `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/3.4/library/tempfile.html#tempfile.mkstemp).16101611Если объект прослушивателя использует сокет, то *backlog* (по умолчанию 1) передаётся методу [`listen()`](https://python-all.ru/3.4/library/socket.html#socket.socket.listen) сокета после его привязки.16121613Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.16141615Если *authkey* является байтовой строкой, то она будет использоваться в качестве ключа аутентификации; в противном случае она должна быть *None*.16161617Если *authkey* равен `None`, а *authenticate* равен `True`, то в качестве ключа аутентификации используется `current_process().authkey`. Если *authkey* равен `None`, а *authenticate* равен `False`, то аутентификация не выполняется. Если аутентификация не удалась, то возбуждается [`AuthenticationError`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.AuthenticationError). См. [*Authentication keys*](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing-auth-keys).16181619#### `accept()`16201621Принимает соединение на привязанном сокете или именованном канале объекта прослушивателя и возвращает объект [`Connection`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection). Если предпринята попытка аутентификации и она не удалась, то возбуждается [`AuthenticationError`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.AuthenticationError).16221623#### `close()`16241625Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.16261627Объекты Listener имеют следующие свойства только для чтения:16281629#### `address`16301631Адрес, используемый объектом Listener.16321633#### `last_accepted`16341635Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то `None`.16361637Новое в версии 3.3: Объекты Listener теперь поддерживают протокол управления контекстом – см. [*Типы менеджеров контекста*](https://python-all.ru/3.4/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__enter__) возвращает объект listener, а [`__exit__()`](https://python-all.ru/3.4/library/stdtypes.html#contextmanager.__exit__) вызывает [`close()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.connection.Listener.close).16381639#### `multiprocessing.connection.wait(object_list, timeout=None)`16401641Ожидает, пока какой-либо объект из *object\_list* не станет готовым. Возвращает список тех объектов из *object\_list*, которые готовы. Если *timeout* – число с плавающей запятой, то вызов блокируется не более чем на указанное количество секунд. Если *timeout* равен `None`, то блокировка будет продолжаться неограниченно долго. Отрицательное значение timeout эквивалентно нулевому.16421643Как в Unix, так и в Windows объект может появиться в *object\_list*, если он16441645- читаемый объект [`Connection`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection);1646- подключённый и читаемый объект [`socket.socket`](https://python-all.ru/3.4/library/socket.html#socket.socket); или1647- атрибут [`sentinel`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.sentinel) объекта [`процесса`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process).16481649Объект соединения или сокета готов, когда из него доступны для чтения данные или другой конец был закрыт.16501651**Unix**: `wait(object_list, timeout)` почти эквивалентна `select.select(object_list, [], [], timeout)`. Разница в том, что если [`select.select()`](https://python-all.ru/3.4/library/select.html#select.select) прерывается сигналом, она может возбудить [`OSError`](https://python-all.ru/3.4/library/exceptions.html#OSError) с кодом ошибки `EINTR`, тогда как [`wait()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.connection.wait) этого не делает.16521653**Windows**: Элемент в *object\_list* должен быть либо целочисленным дескриптором, который может ожидаться (согласно определению, используемому в документации функции Win32 `WaitForMultipleObjects()`), либо объектом с методом `fileno()`, который возвращает дескриптор сокета или дескриптор канала. (Обратите внимание, что дескрипторы каналов и сокетов **не** являются ожидаемыми дескрипторами.)16541655Новое в версии 3.3.16561657**Примеры**16581659Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:16601661```python1662from multiprocessing.connection import Listener1663from array import array16641665address = ('localhost', 6000) # семейство определяется как 'AF_INET'16661667with Listener(address, authkey=b'secret password') as listener:1668 with listener.accept() as conn:1669 print('connection accepted from', listener.last_accepted)16701671 conn.send([2.25, None, 'junk', float])16721673 conn.send_bytes(b'hello')16741675 conn.send_bytes(array('i', [42, 1729]))1676```16771678Следующий код подключается к серверу и получает от него некоторые данные:16791680```python1681from multiprocessing.connection import Client1682from array import array16831684address = ('localhost', 6000)16851686with Client(address, authkey=b'secret password') as conn:1687 print(conn.recv()) # => [2.25, None, 'junk', float]16881689 print(conn.recv_bytes()) # => 'hello'16901691 arr = array('i', [0, 0, 0, 0, 0])1692 print(conn.recv_bytes_into(arr)) # => 81693 print(arr) # => array('i', [42, 1729, 0, 0, 0])1694```16951696Следующий код использует [`wait()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.connection.wait) для ожидания сообщений сразу от нескольких процессов:16971698```python1699import time, random1700from multiprocessing import Process, Pipe, current_process1701from multiprocessing.connection import wait17021703def foo(w):1704 for i in range(10):1705 w.send((i, current_process().name))1706 w.close()17071708if __name__ == '__main__':1709 readers = []17101711 for i in range(4):1712 r, w = Pipe(duplex=False)1713 readers.append(r)1714 p = Process(target=foo, args=(w,))1715 p.start()1716 # Закрываем записывающий конец канала, чтобы убедиться, что1717 # p – единственный процесс, владеющий дескриптором канала. Это1718 # гарантирует, что когда p закроет свой дескриптор для записывающего конца,1719 # wait() немедленно сообщит, что читающий конец готов1720 w.close()17211722 while readers:1723 for r in wait(readers):1724 try:1725 msg = r.recv()1726 except EOFError:1727 readers.remove(r)1728 else:1729 print(msg)1730```17311732#### 17.2.2.10.1. Форматы адресов17331734- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.1735- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.1736- **Адрес `'AF_PIPE'` – это строка вида**17371738 `r'\\.\pipe\PipeName'`17391740 . Чтобы использовать17411742 [`Client()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.connection.Client)17431744 для подключения к именованному каналу на удалённом компьютере с именем17451746 *ServerName*17471748 , следует использовать адрес вида17491750 `r'\\ServerName\pipe\PipeName'`17511752 вместо.17531754Обратите внимание, что любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не адресом `'AF_UNIX'`.17551756### 17.2.2.11. Ключи аутентификации17571758Когда используется [`Connection.recv`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Connection.recv), полученные данные автоматически десериализуются (unpickled). К сожалению, десериализация данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/3.4/library/hmac.html#module-hmac) для обеспечения дайджест-аутентификации.17591760Ключ аутентификации – это байтовая строка, которую можно рассматривать как пароль: после установления соединения обе стороны требуют доказательства, что другая сторона знает ключ аутентификации. (Демонстрация того, что обе стороны используют один и тот же ключ, **не** предполагает отправку ключа по соединению.)17611762Если запрошена аутентификация, но ключ аутентификации не указан, то используется возвращаемое значение `current_process().authkey` (см. [`процесс`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process)). Это значение будет автоматически унаследовано любым объектом [`процесса`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process), который создаёт текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы будут использовать единый ключ аутентификации, который можно применять при установке соединений между ними.17631764Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/3.4/library/os.html#os.urandom).17651766### 17.2.2.12. Логирование17671768Доступна некоторая поддержка логирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/3.4/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому возможно (в зависимости от типа обработчика) перемешивание сообщений от разных процессов.17691770#### `multiprocessing.get_logger()`17711772Возвращает регистратор (logger), используемый модулем [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing). При необходимости будет создан новый.17731774При первом создании регистратор имеет уровень `logging.NOTSET` и не имеет обработчика по умолчанию. Сообщения, отправленные этому регистратору, по умолчанию не распространяются на корневой регистратор.17751776Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.17771778#### `multiprocessing.log_to_stderr()`17791780Эта функция вызывает [`get_logger()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.get_logger), но в дополнение к возврату регистратора, созданного get\_logger, она добавляет обработчик, который отправляет вывод в [`sys.stderr`](https://python-all.ru/3.4/library/sys.html#sys.stderr), используя формат `'[%(levelname)s/%(processName)s] %(message)s'`.17811782Ниже приведён пример сеанса с включённым журналированием:17831784```python1785>>> import multiprocessing, logging1786>>> logger = multiprocessing.log_to_stderr()1787>>> logger.setLevel(logging.INFO)1788>>> logger.warning('doomed')1789[WARNING/MainProcess] doomed1790>>> m = multiprocessing.Manager()1791[INFO/SyncManager-...] child process calling self.run()1792[INFO/SyncManager-...] created temp directory /.../pymp-...1793[INFO/SyncManager-...] manager serving at '/.../listener-...'1794>>> del m1795[INFO/MainProcess] sending shutdown message to manager1796[INFO/SyncManager-...] manager exiting with exitcode 01797```17981799Полную таблицу уровней логирования см. в модуле [`logging`](https://python-all.ru/3.4/library/logging.html#module-logging).18001801### 17.2.2.13. Модуль [`multiprocessing.dummy`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing.dummy)18021803[`multiprocessing.dummy`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing.dummy) воспроизводит API модуля [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing), но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/3.4/library/threading.html#module-threading).18041805## 17.2.3. Рекомендации по программированию18061807Существуют определённые рекомендации и идиомы, которые следует соблюдать при использовании [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing).18081809### 17.2.3.1. Все методы запуска18101811Нижеследующее относится ко всем методам запуска.18121813Избегайте разделяемого состояния18141815> По возможности следует стараться избегать передачи больших объёмов данных между процессами.1816>1817> Вероятно, лучше всего ограничиться использованием очередей или каналов для связи между процессами, а не использовать низкоуровневые примитивы синхронизации.18181819Сериализуемость18201821> Убедитесь, что аргументы методов прокси-объектов сериализуемы.18221823Потокобезопасность прокси18241825> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.1826>1827> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)18281829Присоединение процессов-зомби18301831> На Unix, когда процесс завершается, но к нему ещё не присоединились, он становится зомби. Их никогда не должно быть много, потому что каждый раз, когда запускается новый процесс (или вызывается1832>1833> [`active_children()`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.active_children)1834>1835> ), все завершённые процессы, к которым ещё не присоединились, будут присоединены. Кроме того, вызов1836>1837> [`Process.is_alive`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.is_alive)1838>1839> для завершённого процесса также присоединяет его. Тем не менее, вероятно, хорошей практикой является явное присоединение всех запущенных процессов.18401841Лучше наследовать, чем упаковывать/распаковывать18421843> При использовании методов запуска1844>1845> *spawn*1846>1847> или1848>1849> *forkserver*1850>1851> многие типы из1852>1853> [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing)1854>1855> должны быть сериализуемы с помощью pickle, чтобы дочерние процессы могли их использовать. Однако, как правило, следует избегать отправки разделяемых объектов другим процессам через каналы или очереди. Вместо этого стоит организовать программу так, чтобы процесс, которому требуется доступ к разделяемому ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.18561857Избегайте завершения процессов18581859> Использование метода [`Process.terminate`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.terminate) для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), используемые в данный момент процессом, станут повреждёнными или недоступными для других процессов.1860>1861> Поэтому, вероятно, лучше всего рассматривать использование [`Process.terminate`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.terminate) только для тех процессов, которые никогда не используют никаких разделяемых ресурсов.18621863Присоединение процессов, использующих очереди18641865> Имейте в виду, что процесс, поместивший элементы в очередь, будет ожидать перед завершением, пока все буферизированные элементы не будут переданы потоком-«питателем» в нижележащий канал. (Дочерний процесс может вызвать метод [`Queue.cancel_join_thread`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread) очереди, чтобы избежать такого поведения.)1866>1867> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещённые в очередь, будут в конечном итоге удалены до того, как процесс будет присоединён. В противном случае нельзя быть уверенным, что процессы, поместившие элементы в очередь, завершатся. Также помните, что недемонические процессы присоединяются автоматически.1868>1869> Пример, который приведёт к взаимоблокировке:1870>1871> ```python1872> from multiprocessing import Process, Queue1873>1874> def f(q):1875> q.put('X' * 1000000)1876>1877> if __name__ == '__main__':1878> queue = Queue()1879> p = Process(target=f, args=(queue,))1880> p.start()1881> p.join() # это приводит к взаимоблокировке1882> obj = queue.get()1883> ```1884>1885> Исправлением здесь может быть перестановка двух последних строк (или просто удаление строки `p.join()`).18861887Явная передача ресурсов дочерним процессам18881889> В Unix при использовании метода запуска *fork* дочерний процесс может использовать общий ресурс, созданный в родительском процессе с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.1890>1891> Помимо того, что это делает код (потенциально) совместимым с Windows и другими методами запуска, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если некоторый ресурс освобождается при сборке мусора объекта в родительском процессе.1892>1893> Так, например1894>1895> ```python1896> from multiprocessing import Process, Lock1897>1898> def f():1899> ... do something using "lock" ...1900>1901> if __name__ == '__main__':1902> lock = Lock()1903> for i in range(10):1904> Process(target=f).start()1905> ```1906>1907> следует переписать как1908>1909> ```python1910> from multiprocessing import Process, Lock1911>1912> def f(l):1913> ... do something using "l" ...1914>1915> if __name__ == '__main__':1916> lock = Lock()1917> for i in range(10):1918> Process(target=f, args=(lock,)).start()1919> ```19201921Остерегайтесь замены [`sys.stdin`](https://python-all.ru/3.4/library/sys.html#sys.stdin) на «объект, подобный файлу».19221923> [`multiprocessing`](https://python-all.ru/3.4/library/multiprocessing.html#module-multiprocessing) изначально безусловно вызывал:1924>1925> ```python1926> os.close(sys.stdin.fileno())1927> ```1928>1929> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:1930>1931> ```python1932> sys.stdin.close()1933> sys.stdin = open(os.devnull)1934> ```1935>1936> Это решает фундаментальную проблему конфликта процессов друг с другом, приводящего к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, которые заменяют [`sys.stdin()`](https://python-all.ru/3.4/library/sys.html#sys.stdin) на «объект, подобный файлу» с буферизацией вывода. Эта опасность заключается в том, что если несколько процессов вызовут [`close()`](https://python-all.ru/3.4/library/io.html#io.IOBase.close) для этого объекта, одни и те же данные могут быть сброшены в него несколько раз, что приведёт к повреждению.1937>1938> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:1939>1940> ```python1941> @property1942> def cache(self):1943> pid = os.getpid()1944> if pid != self._pid:1945> self._pid = pid1946> self._cache = []1947> return self._cache1948> ```1949>1950> Дополнительную информацию см. в [issue 5155](https://python-all.ru/3.4/library/multiprocessing.html), [issue 5313](https://python-all.ru/3.4/library/multiprocessing.html) и [issue 5331](https://python-all.ru/3.4/library/multiprocessing.html).19511952### 17.2.3.2. Методы запуска *spawn* и *forkserver*19531954Есть несколько дополнительных ограничений, которые не применяются к *fork* методу запуска.19551956Упаковываемость19571958> Убедитесь, что все аргументы `Process.__init__()` являются picklable. Это означает, в частности, что связанные или несвязанные методы нельзя использовать напрямую в качестве `target` (если только не используется метод запуска *fork*) – просто определите функцию и используйте её вместо этого.1959>1960> Кроме того, при создании подкласса [`Process`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process) необходимо убедиться, что экземпляры будут сериализуемы с помощью pickle при вызове метода [`Process.start`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.start).19611962Глобальные переменные19631964> Имейте в виду, что если код, выполняемый в дочернем процессе, пытается получить доступ к глобальной переменной, то значение, которое он видит (если таковое имеется), может не совпадать со значением в родительском процессе на момент вызова [`Process.start`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.Process.start).1965>1966> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.19671968Безопасный импорт главного модуля19691970> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).1971>1972> Например, использование метода запуска *spawn* или *forkserver* при запуске следующего модуля приведёт к ошибке [`RuntimeError`](https://python-all.ru/3.4/library/exceptions.html#RuntimeError):1973>1974> ```python1975> from multiprocessing import Process1976>1977> def foo():1978> print('hello')1979>1980> p = Process(target=foo)1981> p.start()1982> ```1983>1984> Вместо этого следует защитить «точку входа» программы, используя конструкцию `if __name__ == '__main__':` следующим образом:1985>1986> ```python1987> from multiprocessing import Process, freeze_support, set_start_method1988>1989> def foo():1990> print('hello')1991>1992> if __name__ == '__main__':1993> freeze_support()1994> set_start_method('spawn')1995> p = Process(target=foo)1996> p.start()1997> ```1998>1999> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не в замороженном виде.)2000>2001> Это позволяет новому порождённому интерпретатору Python безопасно импортировать модуль, а затем выполнить функцию `foo()` этого модуля.2002>2003> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.20042005## 17.2.4. Примеры20062007Демонстрация создания и использования настраиваемых менеджеров и прокси:20082009```python2010from multiprocessing import freeze_support2011from multiprocessing.managers import BaseManager, BaseProxy2012import operator20132014##20152016class Foo:2017 def f(self):2018 print('you called Foo.f()')2019 def g(self):2020 print('you called Foo.g()')2021 def _h(self):2022 print('you called Foo._h()')20232024# Простая функция-генератор2025def baz():2026 for i in range(10):2027 yield i*i20282029# Тип прокси для объектов-генераторов2030class GeneratorProxy(BaseProxy):2031 _exposed_ = ['__next__']2032 def __iter__(self):2033 return self2034 def __next__(self):2035 return self._callmethod('__next__')20362037# Функция для возврата модуля operator2038def get_operator_module():2039 return operator20402041##20422043class MyManager(BaseManager):2044 pass20452046# зарегистрировать класс Foo; сделать `f()` и `g()` доступными через прокси2047MyManager.register('Foo1', Foo)20482049# зарегистрировать класс Foo; сделать `g()` и `_h()` доступными через прокси2050MyManager.register('Foo2', Foo, exposed=('g', '_h'))20512052# зарегистрировать функцию-генератор baz; использовать `GeneratorProxy` для создания прокси2053MyManager.register('baz', baz, proxytype=GeneratorProxy)20542055# зарегистрировать get_operator_module(); сделать публичные функции доступными через прокси2056MyManager.register('operator', get_operator_module)20572058##20592060def test():2061 manager = MyManager()2062 manager.start()20632064 print('-' * 20)20652066 f1 = manager.Foo1()2067 f1.f()2068 f1.g()2069 assert not hasattr(f1, '_h')2070 assert sorted(f1._exposed_) == sorted(['f', 'g'])20712072 print('-' * 20)20732074 f2 = manager.Foo2()2075 f2.g()2076 f2._h()2077 assert not hasattr(f2, 'f')2078 assert sorted(f2._exposed_) == sorted(['g', '_h'])20792080 print('-' * 20)20812082 it = manager.baz()2083 for i in it:2084 print('<%d>' % i, end=' ')2085 print()20862087 print('-' * 20)20882089 op = manager.operator()2090 print('op.add(23, 45) =', op.add(23, 45))2091 print('op.pow(2, 94) =', op.pow(2, 94))2092 print('op._exposed_ =', op._exposed_)20932094##20952096if __name__ == '__main__':2097 freeze_support()2098 test()2099```21002101Использование [`Pool`](https://python-all.ru/3.4/library/multiprocessing.html#multiprocessing.pool.Pool):21022103```python2104import multiprocessing2105import time2106import random2107import sys21082109#2110# Функции, используемые тестовым кодом2111#21122113def calculate(func, args):2114 result = func(*args)2115 return '%s says that %s%s = %s' % (2116 multiprocessing.current_process().name,2117 func.__name__, args, result2118 )21192120def calculatestar(args):2121 return calculate(*args)21222123def mul(a, b):2124 time.sleep(0.5 * random.random())2125 return a * b21262127def plus(a, b):2128 time.sleep(0.5 * random.random())2129 return a + b21302131def f(x):2132 return 1.0 / (x - 5.0)21332134def pow3(x):2135 return x ** 321362137def noop(x):2138 pass21392140#2141# Тестовый код2142#21432144def test():2145 PROCESSES = 42146 print('Creating pool with %d processes\n' % PROCESSES)21472148 with multiprocessing.Pool(PROCESSES) as pool:2149 #2150 # Тесты2151 #21522153 TASKS = [(mul, (i, 7)) for i in range(10)] + \2154 [(plus, (i, 8)) for i in range(10)]21552156 results = [pool.apply_async(calculate, t) for t in TASKS]2157 imap_it = pool.imap(calculatestar, TASKS)2158 imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)21592160 print('Ordered results using pool.apply_async():')2161 for r in results:2162 print('\t', r.get())2163 print()21642165 print('Ordered results using pool.imap():')2166 for x in imap_it:2167 print('\t', x)2168 print()21692170 print('Unordered results using pool.imap_unordered():')2171 for x in imap_unordered_it:2172 print('\t', x)2173 print()21742175 print('Ordered results using pool.map() --- will block till complete:')2176 for x in pool.map(calculatestar, TASKS):2177 print('\t', x)2178 print()21792180 #2181 # Тестирование обработки ошибок2182 #21832184 print('Testing error handling:')21852186 try:2187 print(pool.apply(f, (5,)))2188 except ZeroDivisionError:2189 print('\tGot ZeroDivisionError as expected from pool.apply()')2190 else:2191 raise AssertionError('expected ZeroDivisionError')21922193 try:2194 print(pool.map(f, list(range(10))))2195 except ZeroDivisionError:2196 print('\tGot ZeroDivisionError as expected from pool.map()')2197 else:2198 raise AssertionError('expected ZeroDivisionError')21992200 try:2201 print(list(pool.imap(f, list(range(10)))))2202 except ZeroDivisionError:2203 print('\tGot ZeroDivisionError as expected from list(pool.imap())')2204 else:2205 raise AssertionError('expected ZeroDivisionError')22062207 it = pool.imap(f, list(range(10)))2208 for i in range(10):2209 try:2210 x = next(it)2211 except ZeroDivisionError:2212 if i == 5:2213 pass2214 except StopIteration:2215 break2216 else:2217 if i == 5:2218 raise AssertionError('expected ZeroDivisionError')22192220 assert i == 92221 print('\tGot ZeroDivisionError as expected from IMapIterator.next()')2222 print()22232224 #2225 # Тестирование тайм-аутов2226 #22272228 print('Testing ApplyResult.get() with timeout:', end=' ')2229 res = pool.apply_async(calculate, TASKS[0])2230 while 1:2231 sys.stdout.flush()2232 try:2233 sys.stdout.write('\n\t%s' % res.get(0.02))2234 break2235 except multiprocessing.TimeoutError:2236 sys.stdout.write('.')2237 print()2238 print()22392240 print('Testing IMapIterator.next() with timeout:', end=' ')2241 it = pool.imap(calculatestar, TASKS)2242 while 1:2243 sys.stdout.flush()2244 try:2245 sys.stdout.write('\n\t%s' % it.next(0.02))2246 except StopIteration:2247 break2248 except multiprocessing.TimeoutError:2249 sys.stdout.write('.')2250 print()2251 print()22522253if __name__ == '__main__':2254 multiprocessing.freeze_support()2255 test()2256```22572258Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:22592260```python2261import time2262import random22632264from multiprocessing import Process, Queue, current_process, freeze_support22652266#2267# Функция, выполняемая рабочими процессами2268#22692270def worker(input, output):2271 for func, args in iter(input.get, 'STOP'):2272 result = calculate(func, args)2273 output.put(result)22742275#2276# Функция, используемая для вычисления результата2277#22782279def calculate(func, args):2280 result = func(*args)2281 return '%s says that %s%s = %s' % \2282 (current_process().name, func.__name__, args, result)22832284#2285# Функции, на которые ссылаются задачи2286#22872288def mul(a, b):2289 time.sleep(0.5*random.random())2290 return a * b22912292def plus(a, b):2293 time.sleep(0.5*random.random())2294 return a + b22952296#2297#2298#22992300def test():2301 NUMBER_OF_PROCESSES = 42302 TASKS1 = [(mul, (i, 7)) for i in range(20)]2303 TASKS2 = [(plus, (i, 8)) for i in range(10)]23042305 # Создание очередей2306 task_queue = Queue()2307 done_queue = Queue()23082309 # Отправка задач2310 for task in TASKS1:2311 task_queue.put(task)23122313 # Запуск рабочих процессов2314 for i in range(NUMBER_OF_PROCESSES):2315 Process(target=worker, args=(task_queue, done_queue)).start()23162317 # Получение и вывод результатов2318 print('Unordered results:')2319 for i in range(len(TASKS1)):2320 print('\t', done_queue.get())23212322 # Добавить ещё задач с помощью `put()`2323 for task in TASKS2:2324 task_queue.put(task)23252326 # Получить и вывести ещё несколько результатов2327 for i in range(len(TASKS2)):2328 print('\t', done_queue.get())23292330 # Сообщить дочерним процессам об остановке2331 for i in range(NUMBER_OF_PROCESSES):2332 task_queue.put('STOP')23332334if __name__ == '__main__':2335 freeze_support()2336 test()2337```2338