multiprocessing.md
1> **Источник:** https://python-all.ru/2.7/library/multiprocessing.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 16.6. [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) – интерфейс «threading» на основе процессов89Новое в версии 2.6.1011## 16.6.1. Введение1213[`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) – это пакет, который поддерживает порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). Пакет [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) обеспечивает как локальный, так и удалённый параллелизм, эффективно обходя [Глобальную блокировку интерпретатора](https://python-all.ru/2.7/glossary.html#term-global-interpreter-lock), используя подпроцессы вместо потоков. Благодаря этому модуль [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) позволяет программисту полностью использовать несколько процессоров на данной машине. Он работает как на Unix, так и на Windows.1415Модуль [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) также предоставляет API, не имеющие аналогов в модуле [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). Ярким примером является объект `Pool`, который предоставляет удобный способ распараллеливания выполнения функции для нескольких входных значений, распределяя входные данные между процессами (распараллеливание данных). В следующем примере демонстрируется распространённая практика определения таких функций в модуле, чтобы дочерние процессы могли успешно импортировать этот модуль. Этот базовый пример распараллеливания данных с использованием `Pool`,1617```python18from multiprocessing import Pool1920def f(x):21 return x*x2223if __name__ == '__main__':24 p = Pool(5)25 print(p.map(f, [1, 2, 3]))26```2728будет выводить на стандартный вывод2930```python31[1, 4, 9]32```3334### 16.6.1.1. Класс [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process)3536В [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) процессы порождаются созданием объекта [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start). [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) следует API [`threading.Thread`](https://python-all.ru/2.7/library/threading.html#threading.Thread). Простой пример многопроцессной программы:3738```python39from multiprocessing import Process4041def f(name):42 print 'hello', name4344if __name__ == '__main__':45 p = Process(target=f, args=('bob',))46 p.start()47 p.join()48```4950Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:5152```python53from multiprocessing import Process54import os5556def info(title):57 print title58 print 'module name:', __name__59 if hasattr(os, 'getppid'): # доступно только на Unix60 print 'parent process:', os.getppid()61 print 'process id:', os.getpid()6263def f(name):64 info('function f')65 print 'hello', name6667if __name__ == '__main__':68 info('main line')69 p = Process(target=f, args=('bob',))70 p.start()71 p.join()72```7374Объяснение того, почему (в Windows) часть `if __name__ == '__main__'` необходима, см. в [рекомендациях по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming).7576### 16.6.1.2. Обмен объектами между процессами7778[`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) поддерживает два типа каналов связи между процессами:7980**Очереди**8182> Класс [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue) является почти точной копией [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue). Например:83>84> ```python85> from multiprocessing import Process, Queue86>87> def f(q):88> q.put([42, None, 'hello'])89>90> if __name__ == '__main__':91> q = Queue()92> p = Process(target=f, args=(q,))93> p.start()94> print q.get() # печатает "[42, None, 'hello']"95> p.join()96> ```97>98> Очереди потокобезопасны и безопасны для процессов.99100**Каналы**101102> Функция [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является дуплексным (двунаправленным). Например:103>104> ```python105> from multiprocessing import Process, Pipe106>107> def f(conn):108> conn.send([42, None, 'hello'])109> conn.close()110>111> if __name__ == '__main__':112> parent_conn, child_conn = Pipe()113> p = Process(target=f, args=(child_conn,))114> p.start()115> print parent_conn.recv() # печатает "[42, None, 'hello']"116> p.join()117> ```118>119> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы [`send()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.send) и [`recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv) (среди прочих). Обратите внимание: данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.120121### 16.6.1.3. Синхронизация между процессами122123[`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) содержит эквиваленты всех примитивов синхронизации из [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс одновременно выводит на стандартный вывод:124125```python126from multiprocessing import Process, Lock127128def f(l, i):129 l.acquire()130 print 'hello world', i131 l.release()132133if __name__ == '__main__':134 lock = Lock()135136 for num in range(10):137 Process(target=f, args=(lock, num)).start()138```139140Без использования блокировки вывод от разных процессов может полностью перепутаться.141142### 16.6.1.4. Обмен состоянием между процессами143144Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.145146Однако если действительно необходимо использовать некоторые общие данные, то [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) предоставляет несколько способов сделать это.147148**Разделяемая память**149150> Данные могут храниться в отображении разделяемой памяти с помощью [`Value`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Array). Например, следующий код151>152> ```python153> from multiprocessing import Process, Value, Array154>155> def f(n, a):156> n.value = 3.1415927157> for i in range(len(a)):158> a[i] = -a[i]159>160> if __name__ == '__main__':161> num = Value('d', 0.0)162> arr = Array('i', range(10))163>164> p = Process(target=f, args=(num, arr))165> p.start()166> p.join()167>168> print num.value169> print arr[:]170> ```171>172> выведет173>174> ```python175> 3.1415927176> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]177> ```178>179> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, аналогичные используемым модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array): `'d'` означает число с плавающей запятой двойной точности, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасными для процессов и потоков.180>181> Для большей гибкости при работе с разделяемой памятью можно использовать модуль [`multiprocessing.sharedctypes`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.sharedctypes), который поддерживает создание произвольных объектов ctypes, выделенных из разделяемой памяти.182183**Серверный процесс**184185> Объект-менеджер, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими с помощью прокси.186>187> Менеджер, возвращаемый `Manager()`, будет поддерживать типы `list`, [`dict`](https://python-all.ru/2.7/library/stdtypes.html#dict), [`Namespace`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.Namespace), [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Event), [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Array). Например,188>189> ```python190> from multiprocessing import Process, Manager191>192> def f(d, l):193> d[1] = '1'194> d['2'] = 2195> d[0.25] = None196> l.reverse()197>198> if __name__ == '__main__':199> manager = Manager()200>201> d = manager.dict()202> l = manager.list(range(10))203>204> p = Process(target=f, args=(d, l))205> p.start()206> p.join()207>208> print d209> print l210> ```211>212> выведет213>214> ```python215> {0.25: None, 1: '1', '2': 2}216> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]217> ```218>219> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.220221### 16.6.1.5. Использование пула процессов222223Класс `Pool` представляет пул рабочих процессов. Он имеет методы, которые позволяют передавать задачи рабочим процессам несколькими различными способами.224225Например:226227```python228from multiprocessing import Pool, TimeoutError229import time230import os231232def f(x):233 return x*x234235if __name__ == '__main__':236 pool = Pool(processes=4) # запустить 4 рабочих процесса237238 # печатает "[0, 1, 4,..., 81]"239 print pool.map(f, range(10))240241 # печатает те же числа в произвольном порядке242 for i in pool.imap_unordered(f, range(10)):243 print i244245 # вычисляет "f(20)" асинхронно246 res = pool.apply_async(f, (20,)) # выполняется *только* в одном процессе247 print res.get(timeout=1) # печатает "400"248249 # вычисляет "os.getpid()" асинхронно250 res = pool.apply_async(os.getpid, ()) # выполняется *только* в одном процессе251 print res.get(timeout=1) # печатает PID этого процесса252253 # запуск нескольких асинхронных вычислений *может* задействовать больше процессов254 multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]255 print [res.get(timeout=1) for res in multiple_results]256257 # заставить один рабочий процесс спать 10 секунд258 res = pool.apply_async(time.sleep, (10,))259 try:260 print res.get(timeout=1)261 except TimeoutError:262 print "We lacked patience and got a multiprocessing.TimeoutError"263```264265Обратите внимание: методы пула должны использоваться только тем процессом, который его создал.266267> **Примечание**268>269> Для работы функциональности этого пакета требуется, чтобы модуль `__main__` был доступен для импорта дочерними процессами. Это рассматривается в [рекомендациях по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming), но здесь стоит упомянуть об этом. Это означает, что некоторые примеры, такие как примеры с `Pool`, не будут работать в интерактивном интерпретаторе. Например:270>271> ```python272> >>> from multiprocessing import Pool273> >>> p = Pool(5)274> >>> def f(x):275> ... return x*x276> ...277> >>> p.map(f, [1,2,3])278> Process PoolWorker-1:279> Process PoolWorker-2:280> Process PoolWorker-3:281> Traceback (most recent call last):282> AttributeError: 'module' object has no attribute 'f'283> AttributeError: 'module' object has no attribute 'f'284> AttributeError: 'module' object has no attribute 'f'285> ```286>287> (Если попробовать это сделать, на самом деле будут выведены три полные трассировки стека, перемешанные в полу-случайном порядке, и затем, возможно, придётся как-то остановить главный процесс.)288289## 16.6.2. Справочник290291Пакет [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) в основном повторяет API модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).292293### 16.6.2.1. [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) и исключения294295#### `class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})`296297Объекты Process представляют активность, выполняемую в отдельном процессе. Класс [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) имеет эквиваленты всех методов [`threading.Thread`](https://python-all.ru/2.7/library/threading.html#threading.Thread).298299Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должен быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/2.7/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию он равен `None`, то есть ничего не вызывается. *name* – это имя процесса. По умолчанию создаётся уникальное имя вида 'Process-N1:N2:…:Nk', где N1,N2,…,Nk – последовательность целых чисел, длина которой определяется *поколением* процесса. *args* – это кортеж аргументов для вызова цели. *kwargs* – это словарь именованных аргументов для вызова цели. По умолчанию никакие аргументы не передаются в *target*.300301Если подкласс переопределяет конструктор, он должен вызывать конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.302303#### `run()`304305Метод, представляющий активность процесса.306307Этот метод можно переопределить в подклассе. Стандартный метод [`run()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.308309#### `start()`310311Запускает активность процесса.312313Этот метод должен быть вызван не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.314315#### `join([timeout])`316317Блокирует вызывающий поток до завершения процесса, чей метод [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.join) был вызван, или до истечения указанного таймаута.318319Если *timeout* равен `None`, таймаут отсутствует.320321Процесс может быть присоединён много раз.322323Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.324325#### `name`326327Имя процесса.328329Имя – это строка, используемая только для идентификации. Она не несет семантической нагрузки. Несколько процессов могут иметь одинаковое имя. Начальное имя задается конструктором.330331#### `is_alive()`332333Возвращает, жив ли процесс.334335Грубо говоря, объект процесса живёт с момента возврата метода [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.336337#### `daemon`338339Флаг демона процесса (логическое значение). Должен быть установлен до вызова [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start).340341Начальное значение наследуется от создающего процесса.342343Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.344345Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.346347В дополнение к API [`threading.Thread`](https://python-all.ru/2.7/library/threading.html#threading.Thread) объекты [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) также поддерживают следующие атрибуты и методы:348349#### `pid`350351Возвращает идентификатор процесса. До запуска процесса это будет `None`.352353#### `exitcode`354355Код завершения дочернего процесса. Он будет равен `None`, если процесс ещё не завершён. Отрицательное значение *-N* означает, что дочерний процесс был завершён сигналом *N*.356357#### `authkey`358359Ключ аутентификации процесса (байтовая строка).360361При инициализации [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) главному процессу назначается случайная строка с помощью [`os.urandom()`](https://python-all.ru/2.7/library/os.html#os.urandom).362363При создании объекта [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) он наследует ключ аутентификации своего родительского процесса, хотя его можно изменить, установив [`authkey`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.authkey) в другую байтовую строку.364365См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).366367#### `terminate()`368369Завершает процесс. На Unix это делается с помощью сигнала `SIGTERM`; на Windows используется `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.д. выполняться не будут.370371Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.372373> **Предупреждение**374>375> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.376377Обратите внимание: методы [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.is_alive), [`terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) и [`exitcode`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.exitcode) должны вызываться только тем процессом, который создал объект процесса.378379Пример использования некоторых методов [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process):380381```pycon382>>> import multiprocessing, time, signal383>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))384>>> print p, p.is_alive()385<Process(Process-1, initial)> False386>>> p.start()387>>> print p, p.is_alive()388<Process(Process-1, started)> True389>>> p.terminate()390>>> time.sleep(0.1)391>>> print p, p.is_alive()392<Process(Process-1, stopped[SIGTERM])> False393>>> p.exitcode == -signal.SIGTERM394True395```396397#### `exception multiprocessing.BufferTooShort`398399Исключение, возбуждаемое [`Connection.recv_bytes_into()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv_bytes_into), когда предоставленный буфер слишком мал для чтения сообщения.400401Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде байтовой строки.402403### 16.6.2.2. Каналы и очереди404405При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.406407Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает несколько производителей и потребителей).408409Типы [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), [`multiprocessing.queues.SimpleQueue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.multiprocessing.queues.SimpleQueue) и [`JoinableQueue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue) – это очереди FIFO с несколькими производителями и потребителями, основанные на классе [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue) из стандартной библиотеки. Они отличаются тем, что в [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue) отсутствуют методы [`task_done()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.task_done) и [`join()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.join), появившиеся в классе [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue) в Python 2.5.410411Если используется [`JoinableQueue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue), то **обязательно** вызывайте [`JoinableQueue.task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди; в противном случае семафор, используемый для подсчёта незавершённых задач, может переполниться, что приведёт к возбуждению исключения.412413Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [Менеджеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).414415> **Примечание**416>417> [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) использует обычные исключения [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty) и [`Queue.Full`](https://python-all.ru/2.7/library/queue.html#Queue.Full) для сигнализации тайм-аута. Они недоступны в пространстве имён [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing), поэтому их необходимо импортировать из [`Queue`](https://python-all.ru/2.7/library/queue.html#module-Queue).418419> **Примечание**420>421> Когда объект помещается в очередь, он сериализуется с помощью модуля pickle, и фоновый поток позднее сбрасывает сериализованные данные в нижележащий канал. Это имеет некоторые неожиданные последствия, но обычно не вызывает практических трудностей – если это действительно беспокоит, можно вместо этого использовать очередь, созданную с помощью [менеджера](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).422>423> 1. После помещения объекта в пустую очередь может пройти бесконечно малая задержка, прежде чем метод [`empty()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.empty) очереди вернёт [`False`](https://python-all.ru/2.7/library/constants.html#False) и [`get_nowait()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.get_nowait) сможет вернуться без возбуждения [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty).424> 2. Если несколько процессов помещают объекты в очередь, возможно получение объектов на другом конце не по порядку. Однако объекты, помещённые в очередь одним и тем же процессом, всегда будут идти друг за другом в ожидаемом порядке.425426> **Предупреждение**427>428> Если процесс завершается принудительно с помощью [`Process.terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/2.7/library/os.html#os.kill) в то время, как он пытается использовать [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), данные в очереди могут быть повреждены. Это может привести к тому, что любой другой процесс при попытке использовать очередь получит исключение.429430> **Предупреждение**431>432> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не использовал [`JoinableQueue.cancel_join_thread`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread)), он не завершится, пока все буферизованные элементы не будут сброшены в канал.433>434> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.435>436> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [Рекомендации по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming).437438Пример использования очередей для межпроцессного взаимодействия см. в разделе [Примеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-examples).439440#### `multiprocessing.Pipe([duplex])`441442Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection), представляющих концы канала.443444Если *duplex* равно `True` (по умолчанию), то канал двунаправленный. Если *duplex* равно `False`, то канал однонаправленный: `conn1` можно использовать только для получения сообщений, а `conn2` – только для отправки.445446#### `class multiprocessing.Queue([maxsize])`447448Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.449450Обычные исключения [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty) и [`Queue.Full`](https://python-all.ru/2.7/library/queue.html#Queue.Full) из модуля [`Queue`](https://python-all.ru/2.7/library/queue.html#module-Queue) стандартной библиотеки возбуждаются для сигнализации тайм-аутов.451452[`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue) реализует все методы [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue), за исключением [`task_done()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.task_done) и [`join()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.join).453454#### `qsize()`455456Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.457458Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.NotImplementedError) на Unix-платформах, таких как Mac OS X, где `sem_getvalue()` не реализован.459460#### `empty()`461462Возвращает `True`, если очередь пуста, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.463464#### `full()`465466Возвращает `True`, если очередь заполнена, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.467468#### `put(obj[, block[, timeout]])`469470Помещает obj в очередь. Если необязательный аргумент *block* равен `True` (по умолчанию) и *timeout* равен `None` (по умолчанию), при необходимости блокируется до появления свободного места. Если *timeout* положительное число, блокируется не более чем на *timeout* секунд и возбуждает исключение [`Queue.Full`](https://python-all.ru/2.7/library/queue.html#Queue.Full), если за это время свободное место не появилось. В противном случае (*block* равен `False`) помещает элемент в очередь, если свободное место доступно немедленно, иначе возбуждает исключение [`Queue.Full`](https://python-all.ru/2.7/library/queue.html#Queue.Full) (*timeout* в этом случае игнорируется).471472#### `put_nowait(obj)`473474Эквивалентно `put(obj, False)`.475476#### `get([block[, timeout]])`477478Удаляет и возвращает элемент из очереди. Если необязательный аргумент *block* равен `True` (по умолчанию) и *timeout* равен `None` (по умолчанию), при необходимости блокируется до появления элемента. Если *timeout* положительное число, блокируется не более чем на *timeout* секунд и возбуждает исключение [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty), если за это время элемент не появился. В противном случае (block равен `False`) возвращает элемент, если он доступен немедленно, иначе возбуждает исключение [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty) (*timeout* в этом случае игнорируется).479480#### `get_nowait()`481482Эквивалентно `get(False)`.483484[`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue) имеет несколько дополнительных методов, отсутствующих в [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue). Обычно эти методы не нужны в большинстве кода:485486#### `close()`487488Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.489490#### `join_thread()`491492Присоединяет фоновый поток. Этот метод можно использовать только после вызова [`close()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.close). Он блокируется до завершения фонового потока, гарантируя, что все данные из буфера сброшены в канал.493494По умолчанию, если процесс не является создателем очереди, при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы сделать [`join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.join_thread) бездействующим.495496#### `cancel_join_thread()`497498Предотвращает блокировку [`join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.join_thread). В частности, это предотвращает автоматическое присоединение фонового потока при завершении процесса – см. [`join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.join_thread).499500Более подходящим именем для этого метода могло бы быть `allow_exit_without_flush()`. Он, скорее всего, приведёт к потере поставленных в очередь данных, и почти наверняка вам не понадобится его использовать. Он нужен только в том случае, если текущий процесс должен немедленно завершиться, не дожидаясь сброса поставленных в очередь данных в нижележащий канал, и вас не волнует потеря данных.501502> **Примечание**503>504> Функциональность этого класса требует работающей реализации общего семафора в хост-операционной системе. Без неё функциональность класса будет отключена, а попытки создать экземпляр [`Queue`](https://python-all.ru/2.7/library/queue.html#module-Queue) приведут к [`ImportError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ImportError). См. [bpo-3770](https://python-all.ru/2.7/library/multiprocessing.html) для дополнительной информации. То же самое относится и ко всем специализированным типам очередей, перечисленным ниже.505506#### `class multiprocessing.queues.SimpleQueue`507508Это упрощённый тип [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), очень похожий на [`Pipe`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) с блокировкой.509510#### `empty()`511512Возвращает `True`, если очередь пуста, иначе `False`.513514#### `get()`515516Извлекает и возвращает элемент из очереди.517518#### `put(item)`519520Помещает *item* в очередь.521522#### `class multiprocessing.JoinableQueue([maxsize])`523524[`JoinableQueue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue), подкласс [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно имеет методы [`task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.join).525526#### `task_done()`527528Указывает, что ранее поставленная в очередь задача завершена. Используется потоками-потребителями очереди. Для каждого вызова [`get()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.get), использованного для получения задачи, последующий вызов [`task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) сообщает очереди, что обработка задачи завершена.529530Если [`join()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.join) в данный момент заблокирован, он возобновится, когда все элементы будут обработаны (то есть для каждого элемента, который был [`put()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.put) в очередь, был получен вызов [`task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done)).531532Вызывает [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError), если вызывается больше раз, чем было помещено элементов в очередь.533534#### `join()`535536Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.537538Счётчик незавершённых задач увеличивается при каждом добавлении элемента в очередь. Счётчик уменьшается, когда поток-потребитель вызывает [`task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы указать, что элемент получен и вся работа с ним завершена. Когда счётчик незавершённых задач падает до нуля, [`join()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.join) разблокируется.539540### 16.6.2.3. Разное541542#### `multiprocessing.active_children()`543544Возвращает список всех активных дочерних процессов текущего процесса.545546Вызов этой функции имеет побочный эффект – «присоединение» всех уже завершённых процессов.547548#### `multiprocessing.cpu_count()`549550Возвращает количество процессоров в системе. Может вызывать [`NotImplementedError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.NotImplementedError).551552#### `multiprocessing.current_process()`553554Возвращает объект [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.555556Аналог [`threading.current_thread()`](https://python-all.ru/2.7/library/threading.html#threading.current_thread).557558#### `multiprocessing.freeze_support()`559560Добавляет поддержку для случая, когда программа, использующая [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing), была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)561562Эту функцию необходимо вызвать сразу после строки `if __name__ == '__main__'` в главном модуле. Например:563564```python565from multiprocessing import Process, freeze_support566567def f():568 print 'hello world!'569570if __name__ == '__main__':571 freeze_support()572 Process(target=f).start()573```574575Если строка `freeze_support()` опущена, то попытка запустить замороженный исполняемый файл вызовет [`RuntimeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.RuntimeError).576577Вызов `freeze_support()` не имеет эффекта при запуске на любой операционной системе, кроме Windows. Кроме того, если модуль запускается обычным образом интерпретатором Python в Windows (программа не была заморожена), то `freeze_support()` также не имеет эффекта.578579#### `multiprocessing.set_executable()`580581Устанавливает путь к интерпретатору Python, используемому при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/2.7/library/sys.html#sys.executable)). Встраивающим пользователям, вероятно, потребуется сделать что-то вроде582583```python584set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))585```586587прежде чем они смогут создавать дочерние процессы. (Только для Windows)588589> **Примечание**590>591> [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) не содержит аналогов [`threading.active_count()`](https://python-all.ru/2.7/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/2.7/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/2.7/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/2.7/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/2.7/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/2.7/library/threading.html#threading.local).592593### 16.6.2.4. Объекты соединений594595Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.596597Объекты подключения обычно создаются с помощью [`Pipe`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) – см. также [Слушатели и клиенты](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-listeners-clients).598599#### `class Connection`600601#### `send(obj)`602603Отправляет объект на другой конец подключения; его следует читать с помощью [`recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv).604605Объект должен быть сериализуемым. Очень большие сериализованные объекты (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError).606607#### `recv()`608609Возвращает объект, отправленный с другого конца подключения с помощью [`send()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.send). Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего получать и другой конец был закрыт.610611#### `fileno()`612613Возвращает файловый дескриптор или дескриптор (handle), используемый подключением.614615#### `close()`616617Закрывает подключение.618619Вызывается автоматически при сборке мусора для подключения.620621#### `poll([timeout])`622623Возвращает, есть ли данные для чтения.624625Если *timeout* не указан, то метод возвращается немедленно. Если *timeout* – число, то оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, то используется бесконечное ожидание.626627#### `send_bytes(buffer[, offset[, size]])`628629Отправляет байтовые данные из объекта, поддерживающего интерфейс буфера, в виде целого сообщения.630631Если указан *offset*, то данные читаются с этой позиции в *buffer*. Если указан *size*, то из буфера будет прочитано соответствующее количество байт. Очень большие буферы (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError)632633#### `recv_bytes([maxlength])`634635Возвращает полное сообщение из байтовых данных, отправленное с другого конца подключения, в виде строки. Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего получать и другой конец закрыт.636637Если указан *maxlength* и сообщение длиннее *maxlength*, то вызывается [`IOError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.IOError), и подключение больше не будет доступно для чтения.638639#### `recv_bytes_into(buffer[, offset])`640641Читает в *buffer* полное сообщение с байтовыми данными, отправленное с другого конца соединения, и возвращает количество байтов в сообщении. Блокирует выполнение, пока не появится что-то для приёма. Возбуждает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего принимать и другой конец был закрыт.642643*buffer* должен быть объектом, удовлетворяющим интерфейсу буфера для записи. Если задан *offset*, сообщение будет записано в буфер, начиная с этой позиции. Значение offset должно быть неотрицательным целым числом, меньшим длины *buffer* (в байтах).644645Если буфер слишком мал, то вызывается исключение `BufferTooShort` и полное сообщение доступно как `e.args[0]`, где `e` экземпляр исключения.646647Например:648649```pycon650>>> from multiprocessing import Pipe651>>> a, b = Pipe()652>>> a.send([1, 'hello', None])653>>> b.recv()654[1, 'hello', None]655>>> b.send_bytes('thank you')656>>> a.recv_bytes()657'thank you'658>>> import array659>>> arr1 = array.array('i', range(5))660>>> arr2 = array.array('i', [0] * 10)661>>> a.send_bytes(arr1)662>>> count = b.recv_bytes_into(arr2)663>>> assert count == len(arr1) * arr1.itemsize664>>> arr2665array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])666```667668> **Предупреждение**669>670> Метод [`Connection.recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv) автоматически десериализует полученные данные, что может быть угрозой безопасности, если нельзя доверять процессу, отправившему сообщение.671>672> Поэтому, если объект подключения не был создан с помощью `Pipe()` следует использовать методы [`recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv) и [`send()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.send) только после выполнения некоторой аутентификации. См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).673674> **Предупреждение**675>676> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.677678### 16.6.2.5. Примитивы синхронизации679680В целом, в многопроцессных программах примитивы синхронизации требуются не так часто, как в многопоточных. См. документацию модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).681682Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [Менеджеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).683684#### `class multiprocessing.BoundedSemaphore([value])`685686Объект ограниченного семафора: близкий аналог [`threading.BoundedSemaphore`](https://python-all.ru/2.7/library/threading.html#threading.BoundedSemaphore).687688Единственное отличие от его близкого аналога: первый аргумент метода `acquire` называется *block*, и он поддерживает необязательный второй аргумент *timeout*, что согласуется с [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire).689690> **Примечание**691>692> На Mac OS X это неотличимо от [`Semaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализован на этой платформе.693694#### `class multiprocessing.Condition([lock])`695696Условная переменная: клон [`threading.Condition`](https://python-all.ru/2.7/library/threading.html#threading.Condition).697698Если указана *блокировка*, то она должна быть объектом [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) из [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing).699700#### `class multiprocessing.Event`701702Клон [`threading.Event`](https://python-all.ru/2.7/library/threading.html#threading.Event). Этот метод возвращает состояние внутреннего семафора при выходе, поэтому он всегда возвращает `True`, за исключением случаев, когда задан таймаут и операция истекает по таймауту.703704Изменено в версии 2.7: Ранее метод всегда возвращал `None`.705706#### `class multiprocessing.Lock`707708Нерекурсивный объект блокировки: близкий аналог [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock). Когда процесс или поток захватывает блокировку, последующие попытки захватить её из любого процесса или потока будут блокироваться до её освобождения; любой процесс или поток может её освободить. Концепции и поведение [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock) применительно к потокам воспроизводятся здесь в [`multiprocessing.Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) применительно к процессам или потокам, за исключением отмеченных случаев.709710Обратите внимание, что [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.Lock`, инициализированный контекстом по умолчанию.711712[`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) поддерживает протокол [менеджера контекста](https://python-all.ru/2.7/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with).713714#### `acquire(block=True, timeout=None)`715716Захватывает блокировку, блокирующую или неблокирующую.717718Если аргумент *block* установлен в `True` (по умолчанию), вызов метода будет блокироваться, пока блокировка не окажется в разблокированном состоянии, затем установит её в заблокированное состояние и вернёт `True`. Обратите внимание, что имя этого первого аргумента отличается от имени в [`threading.Lock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.Lock.acquire).719720Если аргумент *block* установлен в `False`, вызов метода не блокируется. Если блокировка в данный момент находится в заблокированном состоянии, возвращается `False`; в противном случае блокировка устанавливается в заблокированное состояние и возвращается `True`.721722При вызове с положительным значением с плавающей точкой для *timeout* блокируется не более чем на число секунд, указанное в *timeout*, пока блокировка не будет получена. Вызовы с отрицательным значением для *timeout* эквивалентны *timeout*, равному нулю. Вызовы с *timeout*, равным `None` (по умолчанию), устанавливают период ожидания бесконечным. Аргумент *timeout* не имеет практического значения, если аргумент *block* установлен в `False`, и поэтому игнорируется. Возвращает `True`, если блокировка получена, или `False`, если период ожидания истёк. Обратите внимание, что аргумент *timeout* не существует в аналоге этого метода, [`threading.Lock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.Lock.acquire).723724#### `release()`725726Освобождает блокировку. Может вызываться из любого процесса или потока, а не только из того, который изначально захватил блокировку.727728Поведение аналогично [`threading.Lock.release()`](https://python-all.ru/2.7/library/threading.html#threading.Lock.release) за исключением того, что при вызове на незаблокированной блокировке возбуждается [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError).729730#### `class multiprocessing.RLock`731732Объект рекурсивной блокировки: близкий аналог [`threading.RLock`](https://python-all.ru/2.7/library/threading.html#threading.RLock). Рекурсивная блокировка должна быть освобождена тем же процессом или потоком, который её захватил. После того как процесс или поток захватил рекурсивную блокировку, тот же процесс или поток может захватить её снова без блокировки; этот процесс или поток должен освободить её по одному разу за каждый захват.733734Обратите внимание, что [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.RLock`, инициализированный контекстом по умолчанию.735736[`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) поддерживает протокол [менеджера контекста](https://python-all.ru/2.7/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with).737738#### `acquire(block=True, timeout=None)`739740Захватывает блокировку, блокирующую или неблокирующую.741742При вызове с аргументом *block*, установленным в `True`, блокируется до тех пор, пока блокировка не окажется в разблокированном состоянии (не принадлежит ни одному процессу или потоку), если только блокировка уже не принадлежит текущему процессу или потоку. Затем текущий процесс или поток получает владение блокировкой (если ещё не владеет ею), и уровень рекурсии внутри блокировки увеличивается на единицу, возвращая значение `True`. Обратите внимание, что имеется несколько различий в поведении этого первого аргумента по сравнению с реализацией [`threading.RLock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.RLock.acquire), начиная с названия самого аргумента.743744При вызове с аргументом *block*, установленным в `False`, не блокируется. Если блокировка уже была захвачена (и, следовательно, принадлежит) другим процессом или потоком, текущий процесс или поток не получает владение, а уровень рекурсии внутри блокировки не изменяется, возвращая значение `False`. Если блокировка находится в разблокированном состоянии, текущий процесс или поток получает владение, а уровень рекурсии увеличивается, возвращая значение `True`.745746Использование и поведение аргумента *timeout* такие же, как в [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire). Обратите внимание, что аргумент *timeout* не существует в аналоге этого метода, [`threading.RLock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.RLock.acquire).747748#### `release()`749750Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения уровень рекурсии становится нулевым, переводит блокировку в разблокированное состояние (не принадлежит ни одному процессу или потоку), и если какие-либо другие процессы или потоки ожидают разблокировки, позволяет ровно одному из них продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся заблокированной и принадлежащей вызывающему процессу или потоку.751752Вызывайте этот метод только когда вызывающий процесс или поток владеет блокировкой. Исключение [`AssertionError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AssertionError) возбуждается, если этот метод вызывается процессом или потоком, не являющимся владельцем, или если блокировка находится в разблокированном (непринадлежащем) состоянии. Обратите внимание, что тип возбуждаемого исключения в этой ситуации отличается от реализованного поведения в [`threading.RLock.release()`](https://python-all.ru/2.7/library/threading.html#threading.RLock.release).753754#### `class multiprocessing.Semaphore([value])`755756Объект семафора: близкий аналог [`threading.Semaphore`](https://python-all.ru/2.7/library/threading.html#threading.Semaphore).757758Единственное отличие от его близкого аналога: первый аргумент его метода `acquire` называется *block*, и он поддерживает необязательный второй аргумент *timeout*, в соответствии с [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire).759760> **Примечание**761>762> The `acquire()` method of [`BoundedSemaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) and [`Semaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Semaphore) has a timeout parameter not supported by the equivalents in [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). The signature is `acquire(block=True, timeout=None)` with keyword parameters being acceptable. If *block* is `True` and *timeout* is not `None` then it specifies a timeout in seconds. If *block* is `False` then *timeout* is ignored.763>764> На Mac OS X `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с тайм-аутом будет эмулировать поведение этой функции с помощью спящего цикла.765766> **Примечание**767>768> Если сигнал SIGINT, сгенерированный `Ctrl-C`, поступает, когда главный поток блокирован вызовом `BoundedSemaphore.acquire()`, [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire), [`RLock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock.acquire), `Semaphore.acquire()`, `Condition.acquire()` или `Condition.wait()`, то вызов будет немедленно прерван и возбуждено [`KeyboardInterrupt`](https://python-all.ru/2.7/library/exceptions.html#exceptions.KeyboardInterrupt).769>770> Это отличается от поведения [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading), где SIGINT игнорируется, пока выполняются эквивалентные блокирующие вызовы.771772> **Примечание**773>774> Некоторые функции этого пакета требуют работающей реализации общего семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки импортировать его приведут к [`ImportError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ImportError). См. [bpo-3770](https://python-all.ru/2.7/library/multiprocessing.html) для получения дополнительной информации.775776### 16.6.2.6. Разделяемые [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes) объекты777778Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.779780#### `multiprocessing.Value(typecode_or_type, *args[, lock])`781782Возвращает объект [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes), выделенный из общей памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для этого объекта.783784*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). *\*args* передаётся конструктору типа.785786Если *блокировка* равна `True` (по умолчанию), то создаётся новый рекурсивный объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".787788Операции, подобные `+=`, которые включают чтение и запись, не являются атомарными. Поэтому, если, например, требуется атомарно увеличить разделяемое значение, недостаточно просто сделать789790```python791counter.value += 1792```793794При условии, что связанная блокировка рекурсивна (по умолчанию она такой и является), можно вместо этого сделать795796```python797with counter.get_lock():798 counter.value += 1799```800801Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.802803#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`804805Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.806807*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо ctypes-тип, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.808809Если *блокировка* равен `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* – объект [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равен `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».810811Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.812813Обратите внимание, что массив типа [`ctypes.c_char`](https://python-all.ru/2.7/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.814815#### 16.6.2.6.1. Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.sharedctypes)816817Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.sharedctypes) предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.818819> **Примечание**820>821> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.822823#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`824825Возвращает массив ctypes, выделенный из разделяемой памяти.826827*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.828829Обратите внимание, что установка и получение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.830831#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`832833Возвращает объект ctypes, выделенный из разделяемой памяти.834835*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). *\*args* передаётся конструктору типа.836837Обратите внимание, что установка и получение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.838839Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/2.7/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют использовать его для сохранения и извлечения строк – см. документацию к [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes).840841#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *args[, lock])`842843То же, что и [`RawArray()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного массива ctypes.844845Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".846847Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.848849#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args[, lock])`850851То же, что и [`RawValue()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного объекта ctypes.852853Если *блокировка* равен `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* – объект [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равен `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».854855Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.856857#### `multiprocessing.sharedctypes.copy(obj)`858859Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes *obj*.860861#### `multiprocessing.sharedctypes.synchronized(obj[, lock])`862863Возвращает процесс-безопасную обёртку для объекта ctypes, которая использует *блокировка* для синхронизации доступа. Если *блокировка* равен `None` (по умолчанию), объект [`multiprocessing.RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) создаётся автоматически.864865Синхронизированная обёртка будет иметь два дополнительных метода (помимо методов обёрнутого объекта): `get_obj()` возвращает обёрнутый объект, а `get_lock()` – объект блокировки, используемый для синхронизации.866867Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.868869В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некоторый подкласс [`ctypes.Structure`](https://python-all.ru/2.7/library/ctypes.html#ctypes.Structure).)870871| ctypes | sharedctypes с типом | sharedctypes с typecode |872| --- | --- | --- |873| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |874| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) | |875| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |876| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |877878Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:879880```python881from multiprocessing import Process, Lock882from multiprocessing.sharedctypes import Value, Array883from ctypes import Structure, c_double884885class Point(Structure):886 _fields_ = [('x', c_double), ('y', c_double)]887888def modify(n, x, s, A):889 n.value **= 2890 x.value **= 2891 s.value = s.value.upper()892 for a in A:893 a.x **= 2894 a.y **= 2895896if __name__ == '__main__':897 lock = Lock()898899 n = Value('i', 7)900 x = Value(c_double, 1.0/3.0, lock=False)901 s = Array('c', 'hello world', lock=lock)902 A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)903904 p = Process(target=modify, args=(n, x, s, A))905 p.start()906 p.join()907908 print n.value909 print x.value910 print s.value911 print [(a.x, a.y) for a in A]912```913914Выводятся следующие результаты:915916```text917499180.1111111111111111919HELLO WORLD920[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]921```922923### 16.6.2.7. Менеджеры924925Менеджеры позволяют создавать данные, которыми могут обмениваться разные процессы. Объект менеджера управляет серверным процессом, который ведает *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.926927#### `multiprocessing.Manager()`928929Возвращает запущенный объект [`SyncManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.SyncManager), который можно использовать для совместного использования объектов между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.930931Процессы менеджера завершаются, как только они будут собраны сборщиком мусора или завершится их родительский процесс. Классы менеджера определены в модуле [`multiprocessing.managers`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.managers):932933#### `class multiprocessing.managers.BaseManager([address[, authkey]])`934935Создает объект BaseManager.936937После создания необходимо вызвать [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или `get_server().serve_forever()`, чтобы объект менеджера ссылался на запущенный процесс менеджера.938939*address* – адрес, на котором процесс менеджера ожидает новые подключения. Если *address* равно `None`, то выбирается произвольный адрес.940941*authkey* – это ключ аутентификации, который будет использоваться для проверки подлинности входящих соединений с серверным процессом. Если *authkey* равен `None`, то `current_process().authkey`. В противном случае используется *authkey*, и он должен быть строкой.942943#### `start([initializer[, initargs]])`944945Запускает подпроцесс для запуска менеджера. Если *initializer* не равно `None`, то подпроцесс вызовет `initializer(*initargs)` при запуске.946947#### `get_server()`948949Возвращает объект `Server`, представляющий реальный сервер, управляемый менеджером. Объект `Server` поддерживает метод `serve_forever()`:950951```python952>>> from multiprocessing.managers import BaseManager953>>> manager = BaseManager(address=('', 50000), authkey='abc')954>>> server = manager.get_server()955>>> server.serve_forever()956```957958`Server` также имеет атрибут [`address`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).959960#### `connect()`961962Подключает локальный объект менеджера к удаленному процессу менеджера:963964```python965>>> from multiprocessing.managers import BaseManager966>>> m = BaseManager(address=('127.0.0.1', 5000), authkey='abc')967>>> m.connect()968```969970#### `shutdown()`971972Останавливает процесс, используемый менеджером. Это доступно только если [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.973974Этот метод можно вызывать несколько раз.975976#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`977978Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.979980*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.981982*callable* – это вызываемый объект, используемый для создания объектов данного идентификатора типа. Если экземпляр менеджера будет создан с помощью метода класса `from_address()` или если аргумент *create\_method* равен `False`, то его можно оставить как `None`.983984*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.985986*exposed* используется для указания последовательности имен методов, к которым прокси для данного typeid должны иметь доступ через [`BaseProxy._callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). (Если *exposed* равно `None`, то вместо него используется `proxytype._exposed_`, если он существует.) Если список exposed не указан, то все «публичные методы» разделяемого объекта будут доступны. (Здесь «публичный метод» означает любой атрибут, у которого есть метод [`__call__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__call__) и имя которого не начинается с `'_'`.)987988*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно отображает имена методов в строки typeid. (Если *method\_to\_typeid* равно `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом этого отображения или отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.989990*create\_method* определяет, следует ли создать метод с именем *typeid*, который можно использовать, чтобы указать серверному процессу создать новый разделяемый объект и вернуть для него прокси. По умолчанию `True`.991992Экземпляры [`BaseManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:993994#### `address`995996Адрес, используемый менеджером.997998#### `class multiprocessing.managers.SyncManager`9991000Подкласс [`BaseManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.10011002Он также поддерживает создание разделяемых списков и словарей.10031004#### `BoundedSemaphore([value])`10051006Создать общий объект [`threading.BoundedSemaphore`](https://python-all.ru/2.7/library/threading.html#threading.BoundedSemaphore) и вернуть для него прокси.10071008#### `Condition([lock])`10091010Создать общий объект [`threading.Condition`](https://python-all.ru/2.7/library/threading.html#threading.Condition) и вернуть прокси для него.10111012Если *блокировка* предоставлена, то она должна быть прокси для объекта [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/2.7/library/threading.html#threading.RLock).10131014#### `Event()`10151016Создать общий объект [`threading.Event`](https://python-all.ru/2.7/library/threading.html#threading.Event) и вернуть прокси для него.10171018#### `Lock()`10191020Создать общий объект [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock) и вернуть прокси для него.10211022#### `Namespace()`10231024Создать общий объект [`Namespace`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.Namespace) и вернуть прокси для него.10251026#### `Queue([maxsize])`10271028Создать общий объект [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue) и вернуть прокси для него.10291030#### `RLock()`10311032Создать общий объект [`threading.RLock`](https://python-all.ru/2.7/library/threading.html#threading.RLock) и вернуть прокси для него.10331034#### `Semaphore([value])`10351036Создать общий объект [`threading.Semaphore`](https://python-all.ru/2.7/library/threading.html#threading.Semaphore) и вернуть прокси для него.10371038#### `Array(typecode, sequence)`10391040Создать массив и вернуть для него прокси.10411042#### `Value(typecode, value)`10431044Создать объект с атрибутом `value`, доступным для записи, и вернуть прокси для него.10451046#### `dict()`10471048#### `dict(mapping)`10491050#### `dict(sequence)`10511052Создать общий объект `dict` и вернуть прокси для него.10531054#### `list()`10551056#### `list(sequence)`10571058Создать общий объект `list` и вернуть прокси для него.10591060> **Примечание**1061>1062> Изменения изменяемых значений или элементов в прокси-объектах словарей и списков не будут распространяться через менеджер, поскольку прокси не может отследить, когда его значения или элементы изменяются. Чтобы изменить такой элемент, можно переназначить изменённый объект обратно в прокси-контейнер:1063>1064> ```python1065> # создать прокси для списка и добавить изменяемый объект (словарь)1066> lproxy = manager.list()1067> lproxy.append({})1068> # теперь изменить словарь1069> d = lproxy[0]1070> d['a'] = 11071> d['b'] = 21072> # на данный момент изменения в d еще не синхронизированы, но при1073> # при переприсваивании словаря прокси получает уведомление об изменении1074> lproxy[0] = d1075> ```10761077#### `class multiprocessing.managers.Namespace`10781079Тип, который можно зарегистрировать с помощью [`SyncManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.SyncManager).10801081Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.10821083Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:10841085```pycon1086>>> manager = multiprocessing.Manager()1087>>> Global = manager.Namespace()1088>>> Global.x = 101089>>> Global.y = 'hello'1090>>> Global._z = 12.3 # это атрибут прокси1091>>> print Global1092Namespace(x=10, y='hello')1093```10941095#### 16.6.2.7.1. Настраиваемые менеджеры10961097Чтобы создать собственный менеджер, создаётся подкласс [`BaseManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager) и используется метод класса [`register()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:10981099```python1100from multiprocessing.managers import BaseManager11011102class MathsClass(object):1103 def add(self, x, y):1104 return x + y1105 def mul(self, x, y):1106 return x * y11071108class MyManager(BaseManager):1109 pass11101111MyManager.register('Maths', MathsClass)11121113if __name__ == '__main__':1114 manager = MyManager()1115 manager.start()1116 maths = manager.Maths()1117 print maths.add(4, 3) # выводит 71118 print maths.mul(7, 8) # выводит 561119```11201121#### 16.6.2.7.2. Использование удалённого менеджера11221123Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).11241125Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:11261127```python1128>>> from multiprocessing.managers import BaseManager1129>>> import Queue1130>>> queue = Queue.Queue()1131>>> class QueueManager(BaseManager): pass1132>>> QueueManager.register('get_queue', callable=lambda:queue)1133>>> m = QueueManager(address=('', 50000), authkey='abracadabra')1134>>> s = m.get_server()1135>>> s.serve_forever()1136```11371138Один клиент может получить доступ к серверу следующим образом:11391140```python1141>>> from multiprocessing.managers import BaseManager1142>>> class QueueManager(BaseManager): pass1143>>> QueueManager.register('get_queue')1144>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')1145>>> m.connect()1146>>> queue = m.get_queue()1147>>> queue.put('hello')1148```11491150Другой клиент также может использовать его:11511152```python1153>>> from multiprocessing.managers import BaseManager1154>>> class QueueManager(BaseManager): pass1155>>> QueueManager.register('get_queue')1156>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')1157>>> m.connect()1158>>> queue = m.get_queue()1159>>> queue.get()1160'hello'1161```11621163Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:11641165```python1166>>> from multiprocessing import Process, Queue1167>>> from multiprocessing.managers import BaseManager1168>>> class Worker(Process):1169... def __init__(self, q):1170... self.q = q1171... super(Worker, self).__init__()1172... def run(self):1173... self.q.put('local hello')1174...1175>>> queue = Queue()1176>>> w = Worker(queue)1177>>> w.start()1178>>> class QueueManager(BaseManager): pass1179...1180>>> QueueManager.register('get_queue', callable=lambda: queue)1181>>> m = QueueManager(address=('', 50000), authkey='abracadabra')1182>>> s = m.get_server()1183>>> s.serve_forever()1184```11851186### 16.6.2.8. Прокси-объекты11871188Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.11891190Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:11911192```pycon1193>>> from multiprocessing import Manager1194>>> manager = Manager()1195>>> l = manager.list([i*i for i in range(10)])1196>>> print l1197[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1198>>> print repr(l)1199<ListProxy object, typeid 'list' at 0x...>1200>>> l[4]1201161202>>> l[2:5]1203[4, 9, 16]1204```12051206Обратите внимание, что применение [`str()`](https://python-all.ru/2.7/library/functions.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/2.7/library/functions.html#repr) вернёт представление прокси.12071208Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:12091210```pycon1211>>> a = manager.list()1212>>> b = manager.list()1213>>> a.append(b) # референт a теперь содержит референт b1214>>> print a, b1215[[]] []1216>>> b.append('hello')1217>>> print a, b1218[['hello']] ['hello']1219```12201221> **Примечание**1222>1223> Типы прокси в [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) не поддерживают сравнение по значению. Так, например:1224>1225> ```pycon1226> >>> manager.list([1,2,3]) == [1,2,3]1227> False1228> ```1229>1230> При сравнении следует просто использовать копию референта.12311232#### `class multiprocessing.managers.BaseProxy`12331234Прокси-объекты являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy).12351236#### `_callmethod(methodname[, args[, kwds]])`12371238Вызывает метод референта прокси и возвращает результат.12391240Если `proxy` – это прокси, чей референт – `obj`, то выражение12411242```python1243proxy._callmethod(methodname, args, kwds)1244```12451246вычислит выражение12471248```python1249getattr(obj, methodname)(*args, **kwds)1250```12511252в процессе менеджера.12531254Возвращаемое значение будет копией результата вызова или прокси для нового разделяемого объекта – см. документацию по аргументу *method\_to\_typeid* функции [`BaseManager.register()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).12551256Если при вызове возникло исключение, то оно повторно возбуждается с помощью [`_callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). Если в процессе менеджера возникло другое исключение, то оно преобразуется в исключение `RemoteError` и возбуждается с помощью [`_callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod).12571258Обратите внимание, что исключение будет возбуждаться, если *methodname* не был *экспонирован*.12591260Пример использования [`_callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):12611262```pycon1263>>> l = manager.list(range(10))1264>>> l._callmethod('__len__')1265101266>>> l._callmethod('__getslice__', (2, 7)) # эквивалентно `l[2:7]`1267[2, 3, 4, 5, 6]1268>>> l._callmethod('__getitem__', (20,)) # эквивалентно `l[20]`1269Traceback (most recent call last):1270...1271IndexError: list index out of range1272```12731274#### `_getvalue()`12751276Возвращает копию референта.12771278Если референт не может быть сериализован (unpicklable), то это вызовет исключение.12791280#### `__repr__()`12811282Возвращает представление прокси-объекта.12831284#### `__str__()`12851286Возвращает представление референта.12871288#### 16.6.2.8.1. Очистка12891290Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.12911292Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.12931294### 16.6.2.9. Пулы процессов12951296Можно создать пул процессов, который будет выполнять задачи, отправленные ему с помощью класса `Pool`.12971298#### `class multiprocessing.Pool([processes[, initializer[, initargs[, maxtasksperchild]]]])`12991300Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.13011302*processes* определяет количество используемых рабочих процессов. Если *processes* равно `None`, то используется число, возвращаемое `cpu_count()`. Если *initializer* не равно `None`, то каждый рабочий процесс вызовет `initializer(*initargs)` при запуске.13031304Обратите внимание, что методы объекта пула должны вызываться только тем процессом, который создал пул.13051306Новое в версии 2.7: *maxtasksperchild* – количество задач, которое рабочий процесс может выполнить прежде чем завершится и будет заменён новым рабочим процессом, чтобы освободить неиспользуемые ресурсы. Значение по умолчанию *maxtasksperchild* равно `None`, что означает, что рабочие процессы будут жить столько же, сколько и пул.13071308> **Примечание**1309>1310> Рабочие процессы внутри `Pool` обычно существуют в течение всего времени работы очереди пула. Часто используемый в других системах (например, Apache, mod\_wsgi и т.д.) подход для освобождения ресурсов, удерживаемых рабочими процессами, заключается в том, чтобы позволить рабочему процессу в пуле выполнить только определённый объём работы, после чего он завершается, очищается, и на его место запускается новый процесс. Аргумент *maxtasksperchild* функции `Pool` предоставляет эту возможность конечному пользователю.13111312#### `apply(func[, args[, kwds]])`13131314Эквивалент встроенной функции [`apply()`](https://python-all.ru/2.7/library/functions.html#apply). Она блокируется до получения результата, поэтому [`apply_async()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.apply_async) лучше подходит для выполнения работы в параллельном режиме. Кроме того, *func* выполняется только в одном из рабочих процессов пула.13151316#### `apply_async(func[, args[, kwds[, callback]]])`13171318Вариант метода [`apply()`](https://python-all.ru/2.7/library/functions.html#apply), возвращающий объект результата.13191320Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.13211322#### `map(func, iterable[, chunksize])`13231324Параллельный аналог встроенной функции [`map()`](https://python-all.ru/2.7/library/functions.html#map) (однако поддерживает только один аргумент *iterable*). Блокирует выполнение до готовности результата.13251326Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.13271328#### `map_async(func, iterable[, chunksize[, callback]])`13291330Вариант метода [`map()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map), возвращающий объект результата.13311332Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.13331334#### `imap(func, iterable[, chunksize])`13351336An equivalent of [`itertools.imap()`](https://python-all.ru/2.7/library/itertools.html#itertools.imap).13371338Аргумент *chunksize* совпадает с тем, что используется в методе [`map()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map). Для очень длинных итерируемых объектов использование большого значения *chunksize* может привести к завершению задачи **значительно** быстрее, чем при использовании значения по умолчанию `1`.13391340Кроме того, если *chunksize* равно `1`, то метод `next()` итератора, возвращаемого методом [`imap()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` возбудит `multiprocessing.TimeoutError`, если результат не может быть возвращён в течение *timeout* секунд.13411342#### `imap_unordered(func, iterable[, chunksize])`13431344То же, что и [`imap()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), за исключением того, что порядок результатов из возвращаемого итератора считается произвольным. (Только когда есть только один рабочий процесс, порядок гарантированно будет «правильным».)13451346#### `close()`13471348Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.13491350#### `terminate()`13511352Немедленно останавливает рабочие процессы, не дожидаясь завершения незаконченных задач. Когда объект пула будет собран сборщиком мусора, [`terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate) будет вызван немедленно.13531354#### `join()`13551356Ожидает завершения рабочих процессов. Перед использованием [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.join) необходимо вызвать [`close()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.close) или [`terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate).13571358#### `class multiprocessing.pool.AsyncResult`13591360Класс результата, возвращаемого `Pool.apply_async()` и `Pool.map_async()`.13611362#### `get([timeout])`13631364Возвращает результат, когда он будет получен. Если *timeout* не равен `None` и результат не поступает в течение *timeout* секунд, то вызывается `multiprocessing.TimeoutError`. Если удалённый вызов вызвал исключение, то это исключение будет повторно возбуждено [`get()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get).13651366#### `wait([timeout])`13671368Ожидает, пока результат не станет доступен или пока не пройдёт *timeout* секунд.13691370#### `ready()`13711372Возвращает, завершён ли вызов.13731374#### `successful()`13751376Возвращает, завершился ли вызов без возникновения исключения. Возбуждает [`AssertionError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AssertionError), если результат ещё не готов.13771378Следующий пример демонстрирует использование пула:13791380```python1381from multiprocessing import Pool1382import time13831384def f(x):1385 return x*x13861387if __name__ == '__main__':1388 pool = Pool(processes=4) # запустить 4 рабочих процесса13891390 result = pool.apply_async(f, (10,)) # вычислить "f(10)" асинхронно в одном процессе1391 print result.get(timeout=1) # выводит "100", если компьютер не *очень* медленный13921393 print pool.map(f, range(10)) # выводит "[0, 1, 4,..., 81]"13941395 it = pool.imap(f, range(10))1396 print it.next() # выводит "0"1397 print it.next() # выводит "1"1398 print it.next(timeout=1) # выводит "4", если компьютер не *очень* медленный13991400 result = pool.apply_async(time.sleep, (10,))1401 print result.get(timeout=1) # вызывает multiprocessing.TimeoutError1402```14031404### 16.6.2.10. Слушатели и клиенты14051406Обычно передача сообщений между процессами осуществляется с помощью очередей или с помощью объектов [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection), возвращаемых [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe).14071408Однако модуль [`multiprocessing.connection`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.connection) обеспечивает некоторую дополнительную гибкость. По сути, он предоставляет высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows, а также поддерживает *дайджест-аутентификацию* с помощью модуля [`hmac`](https://python-all.ru/2.7/library/hmac.html#module-hmac).14091410#### `multiprocessing.connection.deliver_challenge(connection, authkey)`14111412Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.14131414Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).14151416#### `multiprocessing.connection.answer_challenge(connection, authkey)`14171418Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.14191420Если приветственное сообщение не получено, то [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError) возбуждается.14211422#### `multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`14231424Пытается установить соединение с слушателем, использующим адрес *address*, возвращая [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection).14251426Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [Address Formats](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-address-formats))14271428Если *authenticate* равно `True` или *authkey* является строкой, то используется дайджест-аутентификация. Ключ аутентификации будет либо *authkey*, либо `current_process().authkey)`, если *authkey* равно `None`. Если аутентификация не удалась, возбуждается [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError). См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).14291430#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`14311432Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.14331434*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.14351436> **Примечание**1437>1438> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.14391440*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них гарантированно доступна только первая. Если *family* равно `None`, то семейство определяется по формату *address*. Если *address* также равно `None`, выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [Форматы адресов](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равно `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/2.7/library/tempfile.html#tempfile.mkstemp).14411442Если объект listener использует сокет, то *backlog* (по умолчанию 1) передаётся методу [`listen()`](https://python-all.ru/2.7/library/socket.html#socket.socket.listen) сокета после его привязки.14431444Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.14451446Если *authkey* является строкой, то она будет использоваться в качестве ключа аутентификации; в противном случае она должна быть `None`.14471448Если *authkey* равно `None` и *authenticate* равно `True`, то `current_process().authkey` используется в качестве ключа аутентификации. Если *authkey* равно `None` и *authenticate* равно `False`, то аутентификация не выполняется. Если аутентификация не удалась, то [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError) возбуждается. См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).14491450#### `accept()`14511452Принимает соединение на привязанном сокете или именованном канале объекта listener и возвращает объект [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection). Если предпринята попытка аутентификации и она не удалась, возбуждается `AuthenticationError`.14531454#### `close()`14551456Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.14571458Объекты Listener имеют следующие свойства только для чтения:14591460#### `address`14611462Адрес, используемый объектом Listener.14631464#### `last_accepted`14651466Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то равен `None`.14671468Модуль определяет следующие исключения:14691470#### `exception multiprocessing.connection.ProcessError`14711472Базовый класс всех исключений [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing).14731474#### `exception multiprocessing.connection.BufferTooShort`14751476Исключение, возбуждаемое [`Connection.recv_bytes_into()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv_bytes_into), когда предоставленный буфер слишком мал для чтения сообщения.14771478#### `exception multiprocessing.connection.AuthenticationError`14791480Возбуждается при ошибке аутентификации.14811482#### `exception multiprocessing.connection.TimeoutError`14831484Возбуждается методами с тайм-аутом по истечении тайм-аута.14851486**Примеры**14871488Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:14891490```python1491from multiprocessing.connection import Listener1492from array import array14931494address = ('localhost', 6000) # семейство определяется как 'AF_INET'1495listener = Listener(address, authkey='secret password')14961497conn = listener.accept()1498print 'connection accepted from', listener.last_accepted14991500conn.send([2.25, None, 'junk', float])15011502conn.send_bytes('hello')15031504conn.send_bytes(array('i', [42, 1729]))15051506conn.close()1507listener.close()1508```15091510Следующий код подключается к серверу и получает от него некоторые данные:15111512```python1513from multiprocessing.connection import Client1514from array import array15151516address = ('localhost', 6000)1517conn = Client(address, authkey='secret password')15181519print conn.recv() # => [2.25, None, 'junk', float]15201521print conn.recv_bytes() # => 'hello'15221523arr = array('i', [0, 0, 0, 0, 0])1524print conn.recv_bytes_into(arr) # => 81525print arr # => array('i', [42, 1729, 0, 0, 0])15261527conn.close()1528```15291530#### 16.6.2.10.1. Форматы адресов15311532- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.1533- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.1534- **Адрес `'AF_PIPE'` – это строка вида**15351536 `r'\.\pipe{PipeName}'`. Чтобы использовать [`Client()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.Client) для подключения к именованному каналу на удалённом компьютере *ServerName*, следует использовать адрес вида `r'\ServerName\pipe{PipeName}'`.15371538Обратите внимание: любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не `'AF_UNIX'`.15391540### 16.6.2.11. Ключи аутентификации15411542При использовании [`Connection.recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv), полученные данные автоматически десериализуются. К сожалению, десериализация данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/2.7/library/hmac.html#module-hmac) для обеспечения дайджест-аутентификации.15431544Ключ аутентификации – это строка, которую можно рассматривать как пароль: как только соединение установлено, обе стороны требуют подтверждения, что другая сторона знает ключ аутентификации. (Доказательство того, что обе стороны используют один и тот же ключ, **не** подразумевает отправку ключа по соединению.)15451546Если запрошена аутентификация, но ключ аутентификации не указан, то используется возвращаемое значение `current_process().authkey` (см. [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process)). Это значение автоматически наследуется любым объектом [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process), который создаёт текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы используют один ключ аутентификации, который можно применять для настройки соединений между ними.15471548Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/2.7/library/os.html#os.urandom).15491550### 16.6.2.12. Логирование15511552Доступна поддержка журналирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/2.7/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому сообщения от разных процессов могут перемешиваться (в зависимости от типа обработчика).15531554#### `multiprocessing.get_logger()`15551556Возвращает логгер, используемый модулем [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing). При необходимости создаётся новый.15571558При первом создании логгер имеет уровень `logging.NOTSET` и не имеет обработчика по умолчанию. Сообщения, отправленные этому логгеру, по умолчанию не распространяются на корневой логгер.15591560Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.15611562#### `multiprocessing.log_to_stderr()`15631564Эта функция вызывает [`get_logger()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.get_logger), но помимо возврата регистратора, созданного get\_logger, добавляет обработчик, который отправляет вывод на [`sys.stderr`](https://python-all.ru/2.7/library/sys.html#sys.stderr), используя формат `'[%(levelname)s/%(processName)s] %(message)s'`.15651566Ниже приведён пример сеанса с включённым журналированием:15671568```python1569>>> import multiprocessing, logging1570>>> logger = multiprocessing.log_to_stderr()1571>>> logger.setLevel(logging.INFO)1572>>> logger.warning('doomed')1573[WARNING/MainProcess] doomed1574>>> m = multiprocessing.Manager()1575[INFO/SyncManager-...] child process calling self.run()1576[INFO/SyncManager-...] created temp directory /.../pymp-...1577[INFO/SyncManager-...] manager serving at '/.../listener-...'1578>>> del m1579[INFO/MainProcess] sending shutdown message to manager1580[INFO/SyncManager-...] manager exiting with exitcode 01581```15821583В дополнение к этим двум функциям логирования, модуль multiprocessing также предоставляет два дополнительных атрибута уровня логирования: `SUBWARNING` и `SUBDEBUG`. В таблице ниже показано, где они находятся в обычной иерархии уровней.15841585| Уровень | Числовое значение |1586| --- | --- |1587| `SUBWARNING` | 25 |1588| `SUBDEBUG` | 5 |15891590Полную таблицу уровней журналирования см. в модуле [`logging`](https://python-all.ru/2.7/library/logging.html#module-logging).15911592Эти дополнительные уровни логирования используются в основном для определённых отладочных сообщений в модуле multiprocessing. Ниже приведён тот же пример, что и выше, но с включённым `SUBDEBUG`:15931594```python1595>>> import multiprocessing, logging1596>>> logger = multiprocessing.log_to_stderr()1597>>> logger.setLevel(multiprocessing.SUBDEBUG)1598>>> logger.warning('doomed')1599[WARNING/MainProcess] doomed1600>>> m = multiprocessing.Manager()1601[INFO/SyncManager-...] child process calling self.run()1602[INFO/SyncManager-...] created temp directory /.../pymp-...1603[INFO/SyncManager-...] manager serving at '/.../pymp-djGBXN/listener-...'1604>>> del m1605[SUBDEBUG/MainProcess] finalizer calling ...1606[INFO/MainProcess] sending shutdown message to manager1607[DEBUG/SyncManager-...] manager received shutdown message1608[SUBDEBUG/SyncManager-...] calling <Finalize object, callback=unlink, ...1609[SUBDEBUG/SyncManager-...] finalizer calling <built-in function unlink> ...1610[SUBDEBUG/SyncManager-...] calling <Finalize object, dead>1611[SUBDEBUG/SyncManager-...] finalizer calling <function rmtree at 0x5aa730> ...1612[INFO/SyncManager-...] manager exiting with exitcode 01613```16141615### 16.6.2.13. Модуль [`multiprocessing.dummy`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.dummy)16161617[`multiprocessing.dummy`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.dummy) воспроизводит API модуля [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing), но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).16181619## 16.6.3. Рекомендации по программированию16201621Существуют определённые рекомендации и идиомы, которых следует придерживаться при использовании [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing).16221623### 16.6.3.1. Все платформы16241625Избегайте разделяемого состояния16261627> По возможности следует стараться избегать передачи больших объёмов данных между процессами.1628>1629> Вероятно, лучше придерживаться использования очередей или каналов для связи между процессами, а не использовать низкоуровневые примитивы синхронизации из модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).16301631Сериализуемость16321633> Убедитесь, что аргументы методов прокси-объектов сериализуемы.16341635Потокобезопасность прокси16361637> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.1638>1639> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)16401641Присоединение процессов-зомби16421643> В Unix, когда процесс завершается, но к нему не присоединились (joined), он становится зомби. Таких процессов не должно быть много, потому что каждый раз при запуске нового процесса (или вызове [`active_children()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.active_children)) все завершённые процессы, к которым ещё не присоединились, будут присоединены. Кроме того, вызов [`Process.is_alive`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.is_alive) для завершённого процесса присоединяет его. Тем не менее, рекомендуется явно присоединять все запущенные процессы.16441645Лучше наследовать, чем упаковывать/распаковывать16461647> В Windows многие типы из [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) должны быть сериализуемыми (picklable), чтобы дочерние процессы могли их использовать. Однако обычно следует избегать отправки общих объектов другим процессам через каналы или очереди. Вместо этого следует организовать программу так, чтобы процесс, которому требуется доступ к общему ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.16481649Избегайте завершения процессов16501651> Использование метода [`Process.terminate`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), которые в данный момент используются процессом, станут повреждёнными или недоступными для других процессов.1652>1653> Поэтому, вероятно, лучше рассматривать использование [`Process.terminate`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) только для процессов, которые никогда не используют разделяемые ресурсы.16541655Присоединение процессов, использующих очереди16561657> Имейте в виду, что процесс, поместивший элементы в очередь, будет ждать перед завершением, пока все буферизированные элементы не будут переданы потоком «feeder» в базовый канал. (Дочерний процесс может вызвать метод [`cancel_join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread) очереди, чтобы избежать такого поведения.)1658>1659> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещённые в очередь, будут в конечном итоге удалены до того, как процесс будет присоединён. В противном случае нельзя быть уверенным, что процессы, поместившие элементы в очередь, завершатся. Также помните, что недемонические процессы присоединяются автоматически.1660>1661> Пример, который приведёт к взаимоблокировке:1662>1663> ```python1664> from multiprocessing import Process, Queue1665>1666> def f(q):1667> q.put('X' * 1000000)1668>1669> if __name__ == '__main__':1670> queue = Queue()1671> p = Process(target=f, args=(queue,))1672> p.start()1673> p.join() # это приводит к взаимоблокировке1674> obj = queue.get()1675> ```1676>1677> Исправление заключается в замене двух последних строк местами (или просто удалении строки с `p.join()`).16781679Явная передача ресурсов дочерним процессам16801681> В Unix дочерний процесс может использовать общий ресурс, созданный в родительском процессе, с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.1682>1683> Помимо обеспечения (потенциальной) совместимости с Windows, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если какой-либо ресурс освобождается при сборке мусора в родительском процессе.1684>1685> Так, например1686>1687> ```python1688> from multiprocessing import Process, Lock1689>1690> def f():1691> ... do something using "lock" ...1692>1693> if __name__ == '__main__':1694> lock = Lock()1695> for i in range(10):1696> Process(target=f).start()1697> ```1698>1699> следует переписать как1700>1701> ```python1702> from multiprocessing import Process, Lock1703>1704> def f(l):1705> ... do something using "l" ...1706>1707> if __name__ == '__main__':1708> lock = Lock()1709> for i in range(10):1710> Process(target=f, args=(lock,)).start()1711> ```17121713Остерегайтесь замены [`sys.stdin`](https://python-all.ru/2.7/library/sys.html#sys.stdin) на «файлоподобный объект»17141715> [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) изначально безусловно вызывал:1716>1717> ```python1718> os.close(sys.stdin.fileno())1719> ```1720>1721> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:1722>1723> ```python1724> sys.stdin.close()1725> sys.stdin = open(os.devnull)1726> ```1727>1728> Это решает основную проблему столкновения процессов друг с другом, приводящую к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, которые заменяют [`sys.stdin()`](https://python-all.ru/2.7/library/sys.html#sys.stdin) на «файлоподобный объект» с буферизацией вывода. Опасность в том, что если несколько процессов вызовут [`close()`](https://python-all.ru/2.7/library/io.html#io.IOBase.close) для этого файлоподобного объекта, одни и те же данные могут быть сброшены в объект несколько раз, что приведёт к повреждению.1729>1730> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:1731>1732> ```python1733> @property1734> def cache(self):1735> pid = os.getpid()1736> if pid != self._pid:1737> self._pid = pid1738> self._cache = []1739> return self._cache1740> ```1741>1742> Для получения дополнительной информации см. [bpo-5155](https://python-all.ru/2.7/library/multiprocessing.html), [bpo-5313](https://python-all.ru/2.7/library/multiprocessing.html) и [bpo-5331](https://python-all.ru/2.7/library/multiprocessing.html)17431744### 16.6.3.2. Windows17451746Поскольку в Windows отсутствует [`os.fork()`](https://python-all.ru/2.7/library/os.html#os.fork), у него есть несколько дополнительных ограничений:17471748Упаковываемость17491750> Убедитесь, что все аргументы `Process.__init__()` сериализуемы (picklable). Это означает, в частности, что связанные или несвязанные методы не могут использоваться напрямую в качестве аргумента `target` в Windows – просто определите функцию и используйте её вместо этого.1751>1752> Кроме того, если вы создаёте подкласс [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process), убедитесь, что экземпляры будут сериализуемы (picklable) при вызове метода [`Process.start`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start).17531754Глобальные переменные17551756> Имейте в виду, что если код, выполняемый в дочернем процессе, попытается обратиться к глобальной переменной, то значение, которое он увидит (если оно есть), может не совпадать со значением в родительском процессе на момент вызова [`Process.start`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start).1757>1758> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.17591760Безопасный импорт главного модуля17611762> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).1763>1764> Например, в Windows при запуске следующего модуля возникнет ошибка [`RuntimeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.RuntimeError):1765>1766> ```python1767> from multiprocessing import Process1768>1769> def foo():1770> print 'hello'1771>1772> p = Process(target=foo)1773> p.start()1774> ```1775>1776> Вместо этого следует защитить «точку входа» программы с помощью `if __name__ == '__main__':` следующим образом:1777>1778> ```python1779> from multiprocessing import Process, freeze_support1780>1781> def foo():1782> print 'hello'1783>1784> if __name__ == '__main__':1785> freeze_support()1786> p = Process(target=foo)1787> p.start()1788> ```1789>1790> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не как замороженное приложение.)1791>1792> Это позволяет только что порождённому интерпретатору Python безопасно импортировать модуль и затем выполнить функцию `foo()` этого модуля.1793>1794> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.17951796## 16.6.4. Примеры17971798Демонстрация создания и использования настраиваемых менеджеров и прокси:17991800```python1801#1802# Этот модуль показывает, как использовать произвольные вызываемые объекты с подклассом `BaseManager`.1803# `BaseManager`.1804#1805# Авторское право (c) 2006-2008, R Oudkerk1806# Все права защищены.1807#18081809from multiprocessing import freeze_support1810from multiprocessing.managers import BaseManager, BaseProxy1811import operator18121813##18141815class Foo(object):1816 def f(self):1817 print 'you called Foo.f()'1818 def g(self):1819 print 'you called Foo.g()'1820 def _h(self):1821 print 'you called Foo._h()'18221823# Простая функция-генератор1824def baz():1825 for i in xrange(10):1826 yield i*i18271828# Тип прокси для объектов-генераторов1829class GeneratorProxy(BaseProxy):1830 _exposed_ = ('next', '__next__')1831 def __iter__(self):1832 return self1833 def next(self):1834 return self._callmethod('next')1835 def __next__(self):1836 return self._callmethod('__next__')18371838# Функция для возврата модуля operator1839def get_operator_module():1840 return operator18411842##18431844class MyManager(BaseManager):1845 pass18461847# зарегистрировать класс Foo; сделать `f()` и `g()` доступными через прокси1848MyManager.register('Foo1', Foo)18491850# зарегистрировать класс Foo; сделать `g()` и `_h()` доступными через прокси1851MyManager.register('Foo2', Foo, exposed=('g', '_h'))18521853# зарегистрировать функцию-генератор baz; использовать `GeneratorProxy` для создания прокси1854MyManager.register('baz', baz, proxytype=GeneratorProxy)18551856# зарегистрировать get_operator_module(); сделать публичные функции доступными через прокси1857MyManager.register('operator', get_operator_module)18581859##18601861def test():1862 manager = MyManager()1863 manager.start()18641865 print '-' * 2018661867 f1 = manager.Foo1()1868 f1.f()1869 f1.g()1870 assert not hasattr(f1, '_h')1871 assert sorted(f1._exposed_) == sorted(['f', 'g'])18721873 print '-' * 2018741875 f2 = manager.Foo2()1876 f2.g()1877 f2._h()1878 assert not hasattr(f2, 'f')1879 assert sorted(f2._exposed_) == sorted(['g', '_h'])18801881 print '-' * 2018821883 it = manager.baz()1884 for i in it:1885 print '<%d>' % i,1886 print18871888 print '-' * 2018891890 op = manager.operator()1891 print 'op.add(23, 45) =', op.add(23, 45)1892 print 'op.pow(2, 94) =', op.pow(2, 94)1893 print 'op.getslice(range(10), 2, 6) =', op.getslice(range(10), 2, 6)1894 print 'op.repeat(range(5), 3) =', op.repeat(range(5), 3)1895 print 'op._exposed_ =', op._exposed_18961897##18981899if __name__ == '__main__':1900 freeze_support()1901 test()1902```19031904Использование `Pool`:19051906```python1907#1908# Тест класса `multiprocessing.Pool`1909#1910# Авторское право (c) 2006-2008, R Oudkerk1911# Все права защищены.1912#19131914import multiprocessing1915import time1916import random1917import sys19181919#1920# Функции, используемые тестовым кодом1921#19221923def calculate(func, args):1924 result = func(*args)1925 return '%s says that %s%s = %s' % (1926 multiprocessing.current_process().name,1927 func.__name__, args, result1928 )19291930def calculatestar(args):1931 return calculate(*args)19321933def mul(a, b):1934 time.sleep(0.5*random.random())1935 return a * b19361937def plus(a, b):1938 time.sleep(0.5*random.random())1939 return a + b19401941def f(x):1942 return 1.0 / (x-5.0)19431944def pow3(x):1945 return x**319461947def noop(x):1948 pass19491950#1951# Тестовый код1952#19531954def test():1955 print 'cpu_count() = %d\n' % multiprocessing.cpu_count()19561957 #1958 # Создать пул1959 #19601961 PROCESSES = 41962 print 'Creating pool with %d processes\n' % PROCESSES1963 pool = multiprocessing.Pool(PROCESSES)1964 print 'pool = %s' % pool1965 print19661967 #1968 # Тесты1969 #19701971 TASKS = [(mul, (i, 7)) for i in range(10)] + \1972 [(plus, (i, 8)) for i in range(10)]19731974 results = [pool.apply_async(calculate, t) for t in TASKS]1975 imap_it = pool.imap(calculatestar, TASKS)1976 imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)19771978 print 'Ordered results using pool.apply_async():'1979 for r in results:1980 print '\t', r.get()1981 print19821983 print 'Ordered results using pool.imap():'1984 for x in imap_it:1985 print '\t', x1986 print19871988 print 'Unordered results using pool.imap_unordered():'1989 for x in imap_unordered_it:1990 print '\t', x1991 print19921993 print 'Ordered results using pool.map() --- will block till complete:'1994 for x in pool.map(calculatestar, TASKS):1995 print '\t', x1996 print19971998 #1999 # Простые тесты производительности2000 #20012002 N = 1000002003 print 'def pow3(x): return x**3'20042005 t = time.time()2006 A = map(pow3, xrange(N))2007 print '\tmap(pow3, xrange(%d)):\n\t\t%s seconds' % \2008 (N, time.time() - t)20092010 t = time.time()2011 B = pool.map(pow3, xrange(N))2012 print '\tpool.map(pow3, xrange(%d)):\n\t\t%s seconds' % \2013 (N, time.time() - t)20142015 t = time.time()2016 C = list(pool.imap(pow3, xrange(N), chunksize=N//8))2017 print '\tlist(pool.imap(pow3, xrange(%d), chunksize=%d)):\n\t\t%s' \2018 ' seconds' % (N, N//8, time.time() - t)20192020 assert A == B == C, (len(A), len(B), len(C))2021 print20222023 L = [None] * 10000002024 print 'def noop(x): pass'2025 print 'L = [None] * 1000000'20262027 t = time.time()2028 A = map(noop, L)2029 print '\tmap(noop, L):\n\t\t%s seconds' % \2030 (time.time() - t)20312032 t = time.time()2033 B = pool.map(noop, L)2034 print '\tpool.map(noop, L):\n\t\t%s seconds' % \2035 (time.time() - t)20362037 t = time.time()2038 C = list(pool.imap(noop, L, chunksize=len(L)//8))2039 print '\tlist(pool.imap(noop, L, chunksize=%d)):\n\t\t%s seconds' % \2040 (len(L)//8, time.time() - t)20412042 assert A == B == C, (len(A), len(B), len(C))2043 print20442045 del A, B, C, L20462047 #2048 # Тестирование обработки ошибок2049 #20502051 print 'Testing error handling:'20522053 try:2054 print pool.apply(f, (5,))2055 except ZeroDivisionError:2056 print '\tGot ZeroDivisionError as expected from pool.apply()'2057 else:2058 raise AssertionError('expected ZeroDivisionError')20592060 try:2061 print pool.map(f, range(10))2062 except ZeroDivisionError:2063 print '\tGot ZeroDivisionError as expected from pool.map()'2064 else:2065 raise AssertionError('expected ZeroDivisionError')20662067 try:2068 print list(pool.imap(f, range(10)))2069 except ZeroDivisionError:2070 print '\tGot ZeroDivisionError as expected from list(pool.imap())'2071 else:2072 raise AssertionError('expected ZeroDivisionError')20732074 it = pool.imap(f, range(10))2075 for i in range(10):2076 try:2077 x = it.next()2078 except ZeroDivisionError:2079 if i == 5:2080 pass2081 except StopIteration:2082 break2083 else:2084 if i == 5:2085 raise AssertionError('expected ZeroDivisionError')20862087 assert i == 92088 print '\tGot ZeroDivisionError as expected from IMapIterator.next()'2089 print20902091 #2092 # Тестирование тайм-аутов2093 #20942095 print 'Testing ApplyResult.get() with timeout:',2096 res = pool.apply_async(calculate, TASKS[0])2097 while 1:2098 sys.stdout.flush()2099 try:2100 sys.stdout.write('\n\t%s' % res.get(0.02))2101 break2102 except multiprocessing.TimeoutError:2103 sys.stdout.write('.')2104 print2105 print21062107 print 'Testing IMapIterator.next() with timeout:',2108 it = pool.imap(calculatestar, TASKS)2109 while 1:2110 sys.stdout.flush()2111 try:2112 sys.stdout.write('\n\t%s' % it.next(0.02))2113 except StopIteration:2114 break2115 except multiprocessing.TimeoutError:2116 sys.stdout.write('.')2117 print2118 print21192120 #2121 # Тестирование колбэка2122 #21232124 print 'Testing callback:'21252126 A = []2127 B = [56, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]21282129 r = pool.apply_async(mul, (7, 8), callback=A.append)2130 r.wait()21312132 r = pool.map_async(pow3, range(10), callback=A.extend)2133 r.wait()21342135 if A == B:2136 print '\tcallbacks succeeded\n'2137 else:2138 print '\t*** callbacks failed\n\t\t%s != %s\n' % (A, B)21392140 #2141 # Проверить, что нет незавершённых задач2142 #21432144 assert not pool._cache, 'cache = %r' % pool._cache21452146 #2147 # Проверить методы close()2148 #21492150 print 'Testing close():'21512152 for worker in pool._pool:2153 assert worker.is_alive()21542155 result = pool.apply_async(time.sleep, [0.5])2156 pool.close()2157 pool.join()21582159 assert result.get() is None21602161 for worker in pool._pool:2162 assert not worker.is_alive()21632164 print '\tclose() succeeded\n'21652166 #2167 # Проверить метод terminate()2168 #21692170 print 'Testing terminate():'21712172 pool = multiprocessing.Pool(2)2173 DELTA = 0.12174 ignore = pool.apply(pow3, [2])2175 results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]2176 pool.terminate()2177 pool.join()21782179 for worker in pool._pool:2180 assert not worker.is_alive()21812182 print '\tterminate() succeeded\n'21832184 #2185 # Проверить сборку мусора2186 #21872188 print 'Testing garbage collection:'21892190 pool = multiprocessing.Pool(2)2191 DELTA = 0.12192 processes = pool._pool2193 ignore = pool.apply(pow3, [2])2194 results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]21952196 results = pool = None21972198 time.sleep(DELTA * 2)21992200 for worker in processes:2201 assert not worker.is_alive()22022203 print '\tgarbage collection succeeded\n'22042205if __name__ == '__main__':2206 multiprocessing.freeze_support()22072208 assert len(sys.argv) in (1, 2)22092210 if len(sys.argv) == 1 or sys.argv[1] == 'processes':2211 print ' Using processes '.center(79, '-')2212 elif sys.argv[1] == 'threads':2213 print ' Using threads '.center(79, '-')2214 import multiprocessing.dummy as multiprocessing2215 else:2216 print 'Usage:\n\t%s [processes | threads]' % sys.argv[0]2217 raise SystemExit(2)22182219 test()2220```22212222Типы синхронизации, такие как блокировки, условия и очереди:22232224```python2225#2226# Тестовый файл для пакета `multiprocessing`2227#2228# Авторское право (c) 2006-2008, R Oudkerk2229# Все права защищены.2230#22312232import time, sys, random2233from Queue import Empty22342235import multiprocessing # может быть перезаписан22362237#### TEST_VALUE22382239def value_func(running, mutex):2240 random.seed()2241 time.sleep(random.random()*4)22422243 mutex.acquire()2244 print '\n\t\t\t' + str(multiprocessing.current_process()) + ' has finished'2245 running.value -= 12246 mutex.release()22472248def test_value():2249 TASKS = 102250 running = multiprocessing.Value('i', TASKS)2251 mutex = multiprocessing.Lock()22522253 for i in range(TASKS):2254 p = multiprocessing.Process(target=value_func, args=(running, mutex))2255 p.start()22562257 while running.value > 0:2258 time.sleep(0.08)2259 mutex.acquire()2260 print running.value,2261 sys.stdout.flush()2262 mutex.release()22632264 print2265 print 'No more running processes'22662267#### TEST_QUEUE22682269def queue_func(queue):2270 for i in range(30):2271 time.sleep(0.5 * random.random())2272 queue.put(i*i)2273 queue.put('STOP')22742275def test_queue():2276 q = multiprocessing.Queue()22772278 p = multiprocessing.Process(target=queue_func, args=(q,))2279 p.start()22802281 o = None2282 while o != 'STOP':2283 try:2284 o = q.get(timeout=0.3)2285 print o,2286 sys.stdout.flush()2287 except Empty:2288 print 'TIMEOUT'22892290 print22912292#### TEST_CONDITION22932294def condition_func(cond):2295 cond.acquire()2296 print '\t' + str(cond)2297 time.sleep(2)2298 print '\tchild is notifying'2299 print '\t' + str(cond)2300 cond.notify()2301 cond.release()23022303def test_condition():2304 cond = multiprocessing.Condition()23052306 p = multiprocessing.Process(target=condition_func, args=(cond,))2307 print cond23082309 cond.acquire()2310 print cond2311 cond.acquire()2312 print cond23132314 p.start()23152316 print 'main is waiting'2317 cond.wait()2318 print 'main has woken up'23192320 print cond2321 cond.release()2322 print cond2323 cond.release()23242325 p.join()2326 print cond23272328#### TEST_SEMAPHORE23292330def semaphore_func(sema, mutex, running):2331 sema.acquire()23322333 mutex.acquire()2334 running.value += 12335 print running.value, 'tasks are running'2336 mutex.release()23372338 random.seed()2339 time.sleep(random.random()*2)23402341 mutex.acquire()2342 running.value -= 12343 print '%s has finished' % multiprocessing.current_process()2344 mutex.release()23452346 sema.release()23472348def test_semaphore():2349 sema = multiprocessing.Semaphore(3)2350 mutex = multiprocessing.RLock()2351 running = multiprocessing.Value('i', 0)23522353 processes = [2354 multiprocessing.Process(target=semaphore_func,2355 args=(sema, mutex, running))2356 for i in range(10)2357 ]23582359 for p in processes:2360 p.start()23612362 for p in processes:2363 p.join()23642365#### TEST_JOIN_TIMEOUT23662367def join_timeout_func():2368 print '\tchild sleeping'2369 time.sleep(5.5)2370 print '\n\tchild terminating'23712372def test_join_timeout():2373 p = multiprocessing.Process(target=join_timeout_func)2374 p.start()23752376 print 'waiting for process to finish'23772378 while 1:2379 p.join(timeout=1)2380 if not p.is_alive():2381 break2382 print '.',2383 sys.stdout.flush()23842385#### TEST_EVENT23862387def event_func(event):2388 print '\t%r is waiting' % multiprocessing.current_process()2389 event.wait()2390 print '\t%r has woken up' % multiprocessing.current_process()23912392def test_event():2393 event = multiprocessing.Event()23942395 processes = [multiprocessing.Process(target=event_func, args=(event,))2396 for i in range(5)]23972398 for p in processes:2399 p.start()24002401 print 'main is sleeping'2402 time.sleep(2)24032404 print 'main is setting event'2405 event.set()24062407 for p in processes:2408 p.join()24092410#### TEST_SHAREDVALUES24112412def sharedvalues_func(values, arrays, shared_values, shared_arrays):2413 for i in range(len(values)):2414 v = values[i][1]2415 sv = shared_values[i].value2416 assert v == sv24172418 for i in range(len(values)):2419 a = arrays[i][1]2420 sa = list(shared_arrays[i][:])2421 assert a == sa24222423 print 'Tests passed'24242425def test_sharedvalues():2426 values = [2427 ('i', 10),2428 ('h', -2),2429 ('d', 1.25)2430 ]2431 arrays = [2432 ('i', range(100)),2433 ('d', [0.25 * i for i in range(100)]),2434 ('H', range(1000))2435 ]24362437 shared_values = [multiprocessing.Value(id, v) for id, v in values]2438 shared_arrays = [multiprocessing.Array(id, a) for id, a in arrays]24392440 p = multiprocessing.Process(2441 target=sharedvalues_func,2442 args=(values, arrays, shared_values, shared_arrays)2443 )2444 p.start()2445 p.join()24462447 assert p.exitcode == 024482449####24502451def test(namespace=multiprocessing):2452 global multiprocessing24532454 multiprocessing = namespace24552456 for func in [ test_value, test_queue, test_condition,2457 test_semaphore, test_join_timeout, test_event,2458 test_sharedvalues ]:24592460 print '\n\t######## %s\n' % func.__name__2461 func()24622463 ignore = multiprocessing.active_children() # очистка всех старых процессов2464 if hasattr(multiprocessing, '_debug_info'):2465 info = multiprocessing._debug_info()2466 if info:2467 print info2468 raise ValueError('there should be no positive refcounts left')24692470if __name__ == '__main__':2471 multiprocessing.freeze_support()24722473 assert len(sys.argv) in (1, 2)24742475 if len(sys.argv) == 1 or sys.argv[1] == 'processes':2476 print ' Using processes '.center(79, '-')2477 namespace = multiprocessing2478 elif sys.argv[1] == 'manager':2479 print ' Using processes and a manager '.center(79, '-')2480 namespace = multiprocessing.Manager()2481 namespace.Process = multiprocessing.Process2482 namespace.current_process = multiprocessing.current_process2483 namespace.active_children = multiprocessing.active_children2484 elif sys.argv[1] == 'threads':2485 print ' Using threads '.center(79, '-')2486 import multiprocessing.dummy as namespace2487 else:2488 print 'Usage:\n\t%s [processes | manager | threads]' % sys.argv[0]2489 raise SystemExit(2)24902491 test(namespace)2492```24932494Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:24952496```python2497#2498# Простой пример использования пула рабочих процессов для выполнения задач.2499#2500# Обратите внимание, что результаты, скорее всего, не будут получены из выходной2501# очереди в том же порядке, в котором соответствующие задачи были2502# помещены во входную очередь. Если важно получить результаты2503# в исходном порядке, рассмотрите использование `Pool.map()` или2504# `Pool.imap()` (что в любом случае сократит объём необходимого кода).2505#2506# Авторское право (c) 2006-2008, R Oudkerk2507# Все права защищены.2508#25092510import time2511import random25122513from multiprocessing import Process, Queue, current_process, freeze_support25142515#2516# Функция, выполняемая рабочими процессами2517#25182519def worker(input, output):2520 for func, args in iter(input.get, 'STOP'):2521 result = calculate(func, args)2522 output.put(result)25232524#2525# Функция, используемая для вычисления результата2526#25272528def calculate(func, args):2529 result = func(*args)2530 return '%s says that %s%s = %s' % \2531 (current_process().name, func.__name__, args, result)25322533#2534# Функции, на которые ссылаются задачи2535#25362537def mul(a, b):2538 time.sleep(0.5*random.random())2539 return a * b25402541def plus(a, b):2542 time.sleep(0.5*random.random())2543 return a + b25442545#2546#2547#25482549def test():2550 NUMBER_OF_PROCESSES = 42551 TASKS1 = [(mul, (i, 7)) for i in range(20)]2552 TASKS2 = [(plus, (i, 8)) for i in range(10)]25532554 # Создание очередей2555 task_queue = Queue()2556 done_queue = Queue()25572558 # Отправка задач2559 for task in TASKS1:2560 task_queue.put(task)25612562 # Запуск рабочих процессов2563 for i in range(NUMBER_OF_PROCESSES):2564 Process(target=worker, args=(task_queue, done_queue)).start()25652566 # Получение и вывод результатов2567 print 'Unordered results:'2568 for i in range(len(TASKS1)):2569 print '\t', done_queue.get()25702571 # Добавить ещё задач с помощью `put()`2572 for task in TASKS2:2573 task_queue.put(task)25742575 # Получить и вывести ещё несколько результатов2576 for i in range(len(TASKS2)):2577 print '\t', done_queue.get()25782579 # Сообщить дочерним процессам об остановке2580 for i in range(NUMBER_OF_PROCESSES):2581 task_queue.put('STOP')25822583if __name__ == '__main__':2584 freeze_support()2585 test()2586```25872588Пример того, как пул рабочих процессов может запускать экземпляр `SimpleHTTPServer.HttpServer`, используя один общий прослушивающий сокет.25892590```python2591#2592# Пример, в котором пул HTTP-серверов использует один общий слушающий сокет2593#2594# В Windows этот модуль зависит от возможности сериализации сокета с помощью pickle2595# объекта, чтобы рабочие процессы могли унаследовать копию сервера2596# объект. (Мы импортируем `multiprocessing.reduction`, чтобы включить эту сериализацию.)2597#2598# Не уверены, нужно ли синхронизировать доступ к методу `socket.accept()` с помощью2599# блокировки, разделяемой между процессами – похоже, это не обязательно.2600#2601# Авторское право (c) 2006-2008, R Oudkerk2602# Все права защищены.2603#26042605import os2606import sys26072608from multiprocessing import Process, current_process, freeze_support2609from BaseHTTPServer import HTTPServer2610from SimpleHTTPServer import SimpleHTTPRequestHandler26112612if sys.platform == 'win32':2613 import multiprocessing.reduction # сделать сокеты сериализуемыми/наследуемыми26142615def note(format, *args):2616 sys.stderr.write('[%s]\t%s\n' % (current_process().name, format%args))26172618class RequestHandler(SimpleHTTPRequestHandler):2619 # мы переопределяем log_message(), чтобы показывать, какой процесс обрабатывает запрос2620 def log_message(self, format, *args):2621 note(format, *args)26222623def serve_forever(server):2624 note('starting server')2625 try:2626 server.serve_forever()2627 except KeyboardInterrupt:2628 pass26292630def runpool(address, number_of_processes):2631 # создать единственный объект сервера – каждый дочерний процесс унаследует его копию2632 server = HTTPServer(address, RequestHandler)26332634 # создать дочерние процессы, которые будут работать как воркеры2635 for i in range(number_of_processes-1):2636 Process(target=serve_forever, args=(server,)).start()26372638 # главный процесс также выступает в роли воркера2639 serve_forever(server)26402641def test():2642 DIR = os.path.join(os.path.dirname(__file__), '..')2643 ADDRESS = ('localhost', 8000)2644 NUMBER_OF_PROCESSES = 426452646 print 'Serving at http://%s:%d using %d worker processes' % \2647 (ADDRESS[0], ADDRESS[1], NUMBER_OF_PROCESSES)2648 print 'To exit press Ctrl-' + ['C', 'Break'][sys.platform=='win32']26492650 os.chdir(DIR)2651 runpool(ADDRESS, NUMBER_OF_PROCESSES)26522653if __name__ == '__main__':2654 freeze_support()2655 test()2656```26572658Несколько простых тестов, сравнивающих [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) с [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading):26592660```python2661#2662# Простые тесты производительности для пакета multiprocessing2663#2664# Авторское право (c) 2006-2008, R Oudkerk2665# Все права защищены.2666#26672668import time, sys, multiprocessing, threading, Queue, gc26692670if sys.platform == 'win32':2671 _timer = time.clock2672else:2673 _timer = time.time26742675delta = 126762677#### TEST_QUEUESPEED26782679def queuespeed_func(q, c, iterations):2680 a = '0' * 2562681 c.acquire()2682 c.notify()2683 c.release()26842685 for i in xrange(iterations):2686 q.put(a)26872688 q.put('STOP')26892690def test_queuespeed(Process, q, c):2691 elapsed = 02692 iterations = 126932694 while elapsed < delta:2695 iterations *= 226962697 p = Process(target=queuespeed_func, args=(q, c, iterations))2698 c.acquire()2699 p.start()2700 c.wait()2701 c.release()27022703 result = None2704 t = _timer()27052706 while result != 'STOP':2707 result = q.get()27082709 elapsed = _timer() - t27102711 p.join()27122713 print iterations, 'objects passed through the queue in', elapsed, 'seconds'2714 print 'average number/sec:', iterations/elapsed27152716#### TEST_PIPESPEED27172718def pipe_func(c, cond, iterations):2719 a = '0' * 2562720 cond.acquire()2721 cond.notify()2722 cond.release()27232724 for i in xrange(iterations):2725 c.send(a)27262727 c.send('STOP')27282729def test_pipespeed():2730 c, d = multiprocessing.Pipe()2731 cond = multiprocessing.Condition()2732 elapsed = 02733 iterations = 127342735 while elapsed < delta:2736 iterations *= 227372738 p = multiprocessing.Process(target=pipe_func,2739 args=(d, cond, iterations))2740 cond.acquire()2741 p.start()2742 cond.wait()2743 cond.release()27442745 result = None2746 t = _timer()27472748 while result != 'STOP':2749 result = c.recv()27502751 elapsed = _timer() - t2752 p.join()27532754 print iterations, 'objects passed through connection in',elapsed,'seconds'2755 print 'average number/sec:', iterations/elapsed27562757#### TEST_SEQSPEED27582759def test_seqspeed(seq):2760 elapsed = 02761 iterations = 127622763 while elapsed < delta:2764 iterations *= 227652766 t = _timer()27672768 for i in xrange(iterations):2769 a = seq[5]27702771 elapsed = _timer()-t27722773 print iterations, 'iterations in', elapsed, 'seconds'2774 print 'average number/sec:', iterations/elapsed27752776#### TEST_LOCK27772778def test_lockspeed(l):2779 elapsed = 02780 iterations = 127812782 while elapsed < delta:2783 iterations *= 227842785 t = _timer()27862787 for i in xrange(iterations):2788 l.acquire()2789 l.release()27902791 elapsed = _timer()-t27922793 print iterations, 'iterations in', elapsed, 'seconds'2794 print 'average number/sec:', iterations/elapsed27952796#### TEST_CONDITION27972798def conditionspeed_func(c, N):2799 c.acquire()2800 c.notify()28012802 for i in xrange(N):2803 c.wait()2804 c.notify()28052806 c.release()28072808def test_conditionspeed(Process, c):2809 elapsed = 02810 iterations = 128112812 while elapsed < delta:2813 iterations *= 228142815 c.acquire()2816 p = Process(target=conditionspeed_func, args=(c, iterations))2817 p.start()28182819 c.wait()28202821 t = _timer()28222823 for i in xrange(iterations):2824 c.notify()2825 c.wait()28262827 elapsed = _timer()-t28282829 c.release()2830 p.join()28312832 print iterations * 2, 'waits in', elapsed, 'seconds'2833 print 'average number/sec:', iterations * 2 / elapsed28342835####28362837def test():2838 manager = multiprocessing.Manager()28392840 gc.disable()28412842 print '\n\t######## testing Queue.Queue\n'2843 test_queuespeed(threading.Thread, Queue.Queue(),2844 threading.Condition())2845 print '\n\t######## testing multiprocessing.Queue\n'2846 test_queuespeed(multiprocessing.Process, multiprocessing.Queue(),2847 multiprocessing.Condition())2848 print '\n\t######## testing Queue managed by server process\n'2849 test_queuespeed(multiprocessing.Process, manager.Queue(),2850 manager.Condition())2851 print '\n\t######## testing multiprocessing.Pipe\n'2852 test_pipespeed()28532854 print28552856 print '\n\t######## testing list\n'2857 test_seqspeed(range(10))2858 print '\n\t######## testing list managed by server process\n'2859 test_seqspeed(manager.list(range(10)))2860 print '\n\t######## testing Array("i", ..., lock=False)\n'2861 test_seqspeed(multiprocessing.Array('i', range(10), lock=False))2862 print '\n\t######## testing Array("i", ..., lock=True)\n'2863 test_seqspeed(multiprocessing.Array('i', range(10), lock=True))28642865 print28662867 print '\n\t######## testing threading.Lock\n'2868 test_lockspeed(threading.Lock())2869 print '\n\t######## testing threading.RLock\n'2870 test_lockspeed(threading.RLock())2871 print '\n\t######## testing multiprocessing.Lock\n'2872 test_lockspeed(multiprocessing.Lock())2873 print '\n\t######## testing multiprocessing.RLock\n'2874 test_lockspeed(multiprocessing.RLock())2875 print '\n\t######## testing lock managed by server process\n'2876 test_lockspeed(manager.Lock())2877 print '\n\t######## testing rlock managed by server process\n'2878 test_lockspeed(manager.RLock())28792880 print28812882 print '\n\t######## testing threading.Condition\n'2883 test_conditionspeed(threading.Thread, threading.Condition())2884 print '\n\t######## testing multiprocessing.Condition\n'2885 test_conditionspeed(multiprocessing.Process, multiprocessing.Condition())2886 print '\n\t######## testing condition managed by a server process\n'2887 test_conditionspeed(multiprocessing.Process, manager.Condition())28882889 gc.enable()28902891if __name__ == '__main__':2892 multiprocessing.freeze_support()2893 test()2894```2895