multiprocessing.md
1> **Источник:** https://python-all.ru/3.1/library/multiprocessing.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 16.3. `multiprocessing` – параллелизм на основе процессов89## 16.3.1. Введение1011`multiprocessing` – это пакет, поддерживающий порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading). Пакет `multiprocessing` предоставляет как локальный, так и удалённый параллелизм, обходя [*глобальную блокировку интерпретатора*](https://python-all.ru/3.1/glossary.html#term-global-interpreter-lock) за счёт использования подпроцессов вместо потоков. Благодаря этому модуль `multiprocessing` позволяет программисту полностью задействовать несколько процессоров на данной машине. Он работает как под Unix, так и под Windows.1213> **Предупреждение**14>15> Часть функционала этого пакета требует работающей реализации разделяемого семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки его импорта приведут к [`ImportError`](https://python-all.ru/3.1/library/exceptions.html#ImportError). Дополнительную информацию см. в [issue 3770](https://python-all.ru/3.1/library/multiprocessing.html).1617> **Примечание**18>19> Функциональность этого пакета требует, чтобы модуль `__main__` был импортируем дочерними процессами. Это описано в [*правилах программирования*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-programming), однако стоит указать это здесь. Это означает, что некоторые примеры, такие как `multiprocessing.Pool`, не будут работать в интерактивном интерпретаторе. Например:20>21> ```python22> >>> from multiprocessing import Pool23> >>> p = Pool(5)24> >>> def f(x):25> ... return x*x26> ...27> >>> p.map(f, [1,2,3])28> Process PoolWorker-1:29> Process PoolWorker-2:30> Process PoolWorker-3:31> Traceback (most recent call last):32> AttributeError: 'module' object has no attribute 'f'33> AttributeError: 'module' object has no attribute 'f'34> AttributeError: 'module' object has no attribute 'f'35> ```36>37> (Если попробовать это сделать, на самом деле будут выведены три полные трассировки стека, перемешанные в полу-случайном порядке, и затем, возможно, придётся как-то остановить главный процесс.)3839### 16.3.1.1. Класс [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process)4041В `multiprocessing` процессы порождаются созданием объекта [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.start). [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process) следует API [`threading.Thread`](https://python-all.ru/3.1/library/threading.html#threading.Thread). Простейший пример многопроцессной программы:4243```python44from multiprocessing import Process4546def f(name):47 print('hello', name)4849if __name__ == '__main__':50 p = Process(target=f, args=('bob',))51 p.start()52 p.join()53```5455Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:5657```python58from multiprocessing import Process59import os6061def info(title):62 print(title)63 print('module name:', __name__)64 print('parent process:', os.getppid())65 print('process id:', os.getpid())6667def f(name):68 info('function f')69 print('hello', name)7071if __name__ == '__main__':72 info('main line')73 p = Process(target=f, args=('bob',))74 p.start()75 p.join()76```7778Зачем (в Windows) нужна конструкция `if __name__ == '__main__'`, объясняется в разделе [*Рекомендации по программированию*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-programming).7980### 16.3.1.2. Обмен объектами между процессами8182`multiprocessing` поддерживает два типа коммуникационных каналов между процессами:8384**Очереди**8586> Класс [`Queue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue) является почти копией [`queue.Queue`](https://python-all.ru/3.1/library/queue.html#queue.Queue). Например:87>88> ```python89> from multiprocessing import Process, Queue90>91> def f(q):92> q.put([42, None, 'hello'])93>94> if __name__ == '__main__':95> q = Queue()96> p = Process(target=f, args=(q,))97> p.start()98> print(q.get()) # печатает "[42, None, 'hello']"99> p.join()100> ```101>102> Очереди потокобезопасны и безопасны для процессов.103104**Каналы**105106> Функция [`Pipe()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является двунаправленным. Например:107>108> ```python109> from multiprocessing import Process, Pipe110>111> def f(conn):112> conn.send([42, None, 'hello'])113> conn.close()114>115> if __name__ == '__main__':116> parent_conn, child_conn = Pipe()117> p = Process(target=f, args=(child_conn,))118> p.start()119> print(parent_conn.recv()) # печатает "[42, None, 'hello']"120> p.join()121> ```122>123> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы [`send()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection.send) и [`recv()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection.recv) (среди прочих). Следует учитывать, что данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.124125### 16.3.1.3. Синхронизация между процессами126127`multiprocessing` содержит аналоги всех примитивов синхронизации из [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс выводит на стандартный вывод за раз:128129```python130from multiprocessing import Process, Lock131132def f(l, i):133 l.acquire()134 print('hello world', i)135 l.release()136137if __name__ == '__main__':138 lock = Lock()139140 for num in range(10):141 Process(target=f, args=(lock, num)).start()142```143144Без использования блокировки вывод от разных процессов может полностью перепутаться.145146### 16.3.1.4. Обмен состоянием между процессами147148Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.149150Однако если всё же требуется использовать общие данные, то `multiprocessing` предоставляет несколько способов сделать это.151152**Разделяемая память**153154> Данные могут храниться в карте разделяемой памяти с помощью [`Value`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Array). Например, следующий код155>156> ```python157> from multiprocessing import Process, Value, Array158>159> def f(n, a):160> n.value = 3.1415927161> for i in range(len(a)):162> a[i] = -a[i]163>164> if __name__ == '__main__':165> num = Value('d', 0.0)166> arr = Array('i', range(10))167>168> p = Process(target=f, args=(num, arr))169> p.start()170> p.join()171>172> print(num.value)173> print(arr[:])174> ```175>176> выведет177>178> ```python179> 3.1415927180> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]181> ```182>183> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, как в модуле [`array`](https://python-all.ru/3.1/library/array.html#module-array): `'d'` обозначает число двойной точности с плавающей точкой, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасны для работы с процессами и потоками.184>185> Для большей гибкости при использовании общей памяти можно применить модуль `multiprocessing.sharedctypes`, который поддерживает создание произвольных объектов ctypes, размещённых в общей памяти.186187**Серверный процесс**188189> Объект менеджера, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими через прокси.190>191> Менеджер, возвращаемый `Manager()`, будет поддерживать типы [`list`](https://python-all.ru/3.1/library/functions.html#list), [`dict`](https://python-all.ru/3.1/library/stdtypes.html#dict), `Namespace`, [`Lock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Event), [`Queue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Array). Например,192>193> ```python194> from multiprocessing import Process, Manager195>196> def f(d, l):197> d[1] = '1'198> d['2'] = 2199> d[0.25] = None200> l.reverse()201>202> if __name__ == '__main__':203> manager = Manager()204>205> d = manager.dict()206> l = manager.list(range(10))207>208> p = Process(target=f, args=(d, l))209> p.start()210> p.join()211>212> print(d)213> print(l)214> ```215>216> выведет217>218> ```python219> {0.25: None, 1: '1', '2': 2}220> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]221> ```222>223> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.224225### 16.3.1.5. Использование пула рабочих процессов226227Класс `Pool` представляет пул рабочих процессов. Он имеет методы, которые позволяют делегировать задачи рабочим процессам несколькими различными способами.228229Например:230231```python232from multiprocessing import Pool233234def f(x):235 return x*x236237if __name__ == '__main__':238 pool = Pool(processes=4) # запустить 4 рабочих процесса239 result = pool.apply_async(f, [10]) # асинхронно вычислить "f(10)"240 print(result.get(timeout=1)) # выводит "100", если компьютер не *очень* медленный241 print(pool.map(f, range(10))) # выводит "[0, 1, 4,..., 81]"242```243244## 16.3.2. Справочник245246Пакет `multiprocessing` в основном повторяет API модуля [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading).247248### 16.3.2.1. [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process) и исключения249250#### `class multiprocessing.Process([group[, target[, name[, args[, kwargs]]]]])`251252Объекты Process представляют активность, которая выполняется в отдельном процессе. Класс [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process) содержит эквиваленты всех методов [`threading.Thread`](https://python-all.ru/3.1/library/threading.html#threading.Thread).253254Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должно быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/3.1/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию он равен `None`, то есть ничего не вызывается. *name* – это имя процесса. По умолчанию генерируется уникальное имя вида 'Process-N1:N2:...:Nk', где N1,N2,...,Nk – последовательность целых чисел, длина которой определяется *поколением* процесса. *args* – кортеж аргументов для вызова цели. *kwargs* – словарь именованных аргументов для вызова цели. По умолчанию в *target* не передается никаких аргументов.255256Если подкласс переопределяет конструктор, он должен убедиться, что вызывает конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.257258#### `run()`259260Метод, представляющий активность процесса.261262Вы можете переопределить этот метод в подклассе. Стандартный метод [`run()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.263264#### `start()`265266Запускает активность процесса.267268Этот метод должен вызываться не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.269270#### `join([timeout])`271272Блокирует вызывающий поток до тех пор, пока процесс, для которого вызван метод [`join()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.join), не завершится или не истечет указанное время ожидания.273274Если *timeout* равен `None`, то время ожидания не ограничено.275276Процесс может быть присоединён много раз.277278Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.279280#### `name`281282Имя процесса.283284Имя – это строка, используемая только для идентификации. Она не несет семантической нагрузки. Несколько процессов могут иметь одинаковое имя. Начальное имя задается конструктором.285286#### `is_alive()`287288Возвращает, жив ли процесс.289290Грубо говоря, объект процесса считается живым с момента возврата метода [`start()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.291292#### `daemon`293294Флаг daemon процесса, логическое значение. Должен быть установлен до вызова [`start()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.start).295296Начальное значение наследуется от создающего процесса.297298Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.299300Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.301302В дополнение к API `Threading.Thread`, объекты [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process) также поддерживают следующие атрибуты и методы:303304#### `pid`305306Возвращает идентификатор процесса. До запуска процесса будет307308`None`309310.311312#### `exitcode`313314Код завершения дочернего процесса. Будет315316`None`317318, если процесс ещё не завершился. Отрицательное значение319320*-N*321322указывает, что дочерний процесс был завершён сигналом323324*N*325326.327328#### `authkey`329330Ключ аутентификации процесса (байтовая строка).331332При инициализации `multiprocessing` главному процессу назначается случайная строка с помощью `os.random()`.333334При создании объекта [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process) он наследует ключ аутентификации своего родительского процесса, хотя это можно изменить, установив [`authkey`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.authkey) в другую строку байтов.335336См. [*Ключи аутентификации*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-auth-keys).337338#### `terminate()`339340Завершает процесс. В Unix для этого используется сигнал `SIGTERM`; в Windows – `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.п. выполнены не будут.341342Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.343344> **Предупреждение**345>346> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.347348Обратите внимание, что методы [`start()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.is_alive) и `exit_code` должны вызываться только тем процессом, который создал объект процесса.349350Пример использования некоторых методов [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process):351352```python353>>> import multiprocessing, time, signal354>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))355>>> print(p, p.is_alive())356<Process(Process-1, initial)> False357>>> p.start()358>>> print(p, p.is_alive())359<Process(Process-1, started)> True360>>> p.terminate()361>>> time.sleep(0.1)362>>> print(p, p.is_alive())363<Process(Process-1, stopped[SIGTERM])> False364>>> p.exitcode == -signal.SIGTERM365True366```367368#### `exception multiprocessing.BufferTooShort`369370Исключение, вызываемое [`Connection.recv_bytes_into()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection.recv_bytes_into), когда переданный объект буфера слишком мал для читаемого сообщения.371372Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде строки байтов.373374### 16.3.2.2. Каналы и очереди375376При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.377378Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает множество производителей и потребителей).379380Типы [`Queue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue) и [`JoinableQueue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue) – это очереди FIFO с множеством производителей и множеством потребителей, построенные по образцу класса [`queue.Queue`](https://python-all.ru/3.1/library/queue.html#queue.Queue) из стандартной библиотеки. Они различаются тем, что [`Queue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue) не содержит методов [`task_done()`](https://python-all.ru/3.1/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.1/library/queue.html#queue.Queue.join), добавленных в класс [`queue.Queue`](https://python-all.ru/3.1/library/queue.html#queue.Queue) в Python 2.5.381382Если используется [`JoinableQueue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue), то **необходимо** вызывать [`JoinableQueue.task_done()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди; иначе семафор, используемый для подсчёта незавершённых задач, может в конечном итоге переполниться, вызвав исключение.383384Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [*Менеджеры*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-managers).385386> **Примечание**387>388> `multiprocessing` использует обычные исключения [`queue.Empty`](https://python-all.ru/3.1/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.1/library/queue.html#queue.Full) для сигнализации о тайм-ауте. Они недоступны в пространстве имён `multiprocessing`, поэтому их необходимо импортировать из [`queue`](https://python-all.ru/3.1/library/queue.html#module-queue).389390> **Предупреждение**391>392> Если процесс завершается с помощью [`Process.terminate()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/3.1/library/os.html#os.kill) в то время, как он пытается использовать [`Queue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue), то данные в очереди, скорее всего, окажутся повреждены. Это может привести к тому, что любой другой процесс получит исключение при последующей попытке использовать эту очередь.393394> **Предупреждение**395>396> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не вызывал `JoinableQueue.cancel_join_thread()`), то этот процесс не завершится, пока все буферизованные элементы не будут сброшены в канал.397>398> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.399>400> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [*Рекомендации по программированию*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-programming).401402Пример использования очередей для межпроцессного взаимодействия см. в разделе [*Примеры*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-examples).403404#### `multiprocessing.Pipe([duplex])`405406Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection), представляющих концы канала.407408Если *duplex* равен `True` (по умолчанию), то канал двунаправленный. Если *duplex* равен `False`, то канал однонаправленный: `conn1` может использоваться только для получения сообщений, а `conn2` – только для отправки сообщений.409410#### `class multiprocessing.Queue([maxsize])`411412Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.413414Обычные исключения [`queue.Empty`](https://python-all.ru/3.1/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.1/library/queue.html#queue.Full) из модуля `Queue` стандартной библиотеки возбуждаются для указания тайм-аутов.415416[`Queue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue) реализует все методы [`queue.Queue`](https://python-all.ru/3.1/library/queue.html#queue.Queue), за исключением [`task_done()`](https://python-all.ru/3.1/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.1/library/queue.html#queue.Queue.join).417418#### `qsize()`419420Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.421422Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/3.1/library/exceptions.html#NotImplementedError) на платформах Unix, таких как Mac OS X, где `sem_getvalue()` не реализован.423424#### `empty()`425426Возвращает427428`True`429430, если очередь пуста, и431432`False`433434в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.435436#### `full()`437438Возвращает439440`True`441442, если очередь полна, и443444`False`445446в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.447448#### `put(item[, block[, timeout]])`449450Помещает элемент в очередь. Если необязательный аргумент451452*block*453454равен455456`True`457458(по умолчанию), а459460*timeout*461462равен463464`None`465466(по умолчанию), то блокируется при необходимости до появления свободного места. Если467468*timeout*469470– положительное число, то блокируется не более чем на471472*timeout*473474секунд и возбуждает исключение475476[`queue.Full`](https://python-all.ru/3.1/library/queue.html#queue.Full)477478, если свободное место не появилось за это время. В противном случае (479480*block*481482равен483484`False`485486), помещает элемент в очередь, если свободное место доступно немедленно, иначе возбуждает исключение487488[`queue.Full`](https://python-all.ru/3.1/library/queue.html#queue.Full)489490(в этом случае491492*timeout*493494игнорируется).495496#### `put_nowait(item)`497498Эквивалентно499500`put(item, False)`501502.503504#### `get([block[, timeout]])`505506Remove and return an item from the queue. If optional args507508*block*509510is511512`True`513514(the default) and515516*timeout*517518is519520`None`521522(the default), block if necessary until an item is available. If523524*timeout*525526is a positive number, it blocks at most527528*timeout*529530seconds and raises the531532[`queue.Empty`](https://python-all.ru/3.1/library/queue.html#queue.Empty)533534exception if no item was available within that time. Otherwise (block is535536`False`537538), return an item if one is immediately available, else raise the539540[`queue.Empty`](https://python-all.ru/3.1/library/queue.html#queue.Empty)541542exception (543544*timeout*545546is ignored in that case).547548#### `get_nowait()`549550#### `get_no_wait()`551552Эквивалентно553554`get(False)`555556.557558У [`multiprocessing.Queue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue) есть несколько дополнительных методов, отсутствующих в [`queue.Queue`](https://python-all.ru/3.1/library/queue.html#queue.Queue). В большинстве кода эти методы обычно не нужны:559560#### `close()`561562Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.563564#### `join_thread()`565566Присоединяет фоновый поток. Используется только после вызова [`close()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue.close). Блокирует выполнение до завершения фонового потока, гарантируя, что все данные из буфера будут сброшены в канал.567568По умолчанию, если процесс не является создателем очереди, то при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы [`join_thread()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue.join_thread) не делала ничего.569570#### `cancel_join_thread()`571572Предотвращает блокирование573574[`join_thread()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue.join_thread)575576. В частности, это не даёт фоновому потоку автоматически присоединиться при завершении процесса – см.577578[`join_thread()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue.join_thread)579580.581582#### `class multiprocessing.JoinableQueue([maxsize])`583584[`JoinableQueue`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue), подкласс [`очереди`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно содержит методы [`task_done()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue.join).585586#### `task_done()`587588Указывает, что ранее поставленная в очередь задача выполнена. Используется потоками-потребителями очереди. Для каждого вызова [`get()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue.get), используемого для получения задачи, последующий вызов [`task_done()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) сообщает очереди, что обработка задачи завершена.589590Если `join()` в данный момент блокируется, он возобновит работу, когда все элементы будут обработаны (т.е. когда для каждого элемента, помещенного в очередь с помощью [`put()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Queue.put), будет получен вызов [`task_done()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done)).591592Вызывает [`ValueError`](https://python-all.ru/3.1/library/exceptions.html#ValueError), если вызывается больше раз, чем было помещено элементов в очередь.593594#### `join()`595596Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.597598Счетчик незавершенных задач увеличивается при каждом добавлении элемента в очередь. Он уменьшается, когда поток-потребитель вызывает [`task_done()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы указать, что элемент получен и вся работа с ним завершена. Когда счетчик незавершенных задач падает до нуля, `join()` разблокируется.599600### 16.3.2.3. Разное601602#### `multiprocessing.active_children()`603604Возвращает список всех активных дочерних процессов текущего процесса.605606Вызов этой функции приводит к «присоединению» любых процессов, которые уже завершились.607608#### `multiprocessing.cpu_count()`609610Возвращает количество процессоров в системе. Может вызвать611612[`NotImplementedError`](https://python-all.ru/3.1/library/exceptions.html#NotImplementedError)613614.615616#### `multiprocessing.current_process()`617618Возвращает объект [`процесса`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.619620Аналог [`threading.current_thread()`](https://python-all.ru/3.1/library/threading.html#threading.current_thread).621622#### `multiprocessing.freeze_support()`623624Добавляет поддержку для случая, когда программа, использующая `multiprocessing`, была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)625626Эту функцию нужно вызывать сразу после строки `if __name__ == '__main__'` главного модуля. Например:627628```python629from multiprocessing import Process, freeze_support630631def f():632 print('hello world!')633634if __name__ == '__main__':635 freeze_support()636 Process(target=f).start()637```638639Если строку `freeze_support()` опустить, то при попытке запустить замороженный исполняемый файл будет вызвано [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError).640641Если модуль запускается обычным образом интерпретатором Python, то [`freeze_support()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.freeze_support) не действует.642643#### `multiprocessing.set_executable()`644645Задаёт путь к интерпретатору Python, который будет использоваться при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/3.1/library/sys.html#sys.executable)). Встраивающим Python разработчикам, вероятно, потребуется сделать что-то вроде646647```python648setExecutable(os.path.join(sys.exec_prefix, 'pythonw.exe'))649```650651прежде чем они смогут создавать дочерние процессы. (Только для Windows)652653> **Примечание**654>655> `multiprocessing` не содержит аналогов [`threading.active_count()`](https://python-all.ru/3.1/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/3.1/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/3.1/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/3.1/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/3.1/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/3.1/library/threading.html#threading.local).656657### 16.3.2.4. Объекты соединений658659Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.660661Объекты соединения обычно создаются с помощью [`Pipe()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Pipe) – см. также [*Listeners and Clients*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-listeners-clients).662663#### `class multiprocessing.Connection`664665#### `send(obj)`666667Отправляет объект на другой конец соединения; этот объект должен быть прочитан с помощью [`recv()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection.recv).668669Объект должен быть сериализуем (picklable). Очень большие объекты (примерно от 32 МБ, хотя это зависит от ОС) могут вызвать исключение ValueError.670671#### `recv()`672673Возвращает объект, отправленный с другого конца соединения с помощью674675[`send()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection.send)676677. Возбуждает678679[`EOFError`](https://python-all.ru/3.1/library/exceptions.html#EOFError)680681, если больше нечего получать и другой конец был закрыт.682683#### `fileno()`684685Возвращает файловый дескриптор или дескриптор, используемый соединением.686687#### `close()`688689Закрывает подключение.690691Вызывается автоматически при сборке мусора для подключения.692693#### `poll([timeout])`694695Возвращает, есть ли данные для чтения.696697Если *timeout* не указан, то возврат происходит немедленно. Если *timeout* – число, оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, используется бесконечное ожидание.698699#### `send_bytes(buffer[, offset[, size]])`700701Отправляет байтовые данные из объекта, поддерживающего интерфейс буфера, в виде целого сообщения.702703Если указан *offset*, данные читаются с этой позиции в *buffer*. Если указан *size*, то из буфера будет прочитано соответствующее количество байт. Очень большие буферы (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение ValueError.704705#### `recv_bytes([maxlength])`706707Возвращает полное сообщение с байтовыми данными, отправленное с другого конца соединения, в виде строки. Возбуждает [`EOFError`](https://python-all.ru/3.1/library/exceptions.html#EOFError), если больше нечего получать и другой конец был закрыт.708709Если задан *maxlength* и сообщение длиннее *maxlength*, то генерируется [`IOError`](https://python-all.ru/3.1/library/exceptions.html#IOError), и соединение становится непригодным для чтения.710711#### `recv_bytes_into(buffer[, offset])`712713Считывает в *buffer* полное сообщение с байтовыми данными, отправленное с другого конца соединения, и возвращает количество байт в сообщении. Возбуждает [`EOFError`](https://python-all.ru/3.1/library/exceptions.html#EOFError), если больше нечего получать и другой конец был закрыт.714715*buffer* должен быть объектом, удовлетворяющим интерфейсу буфера для записи. Если задан *offset*, сообщение будет записано в буфер, начиная с этой позиции. Значение offset должно быть неотрицательным целым числом, меньшим длины *buffer* (в байтах).716717Если буфер слишком мал, то вызывается исключение [`BufferTooShort`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.BufferTooShort), и полное сообщение доступно как `e.args[0]`, где `e` – это экземпляр исключения.718719Например:720721```python722>>> from multiprocessing import Pipe723>>> a, b = Pipe()724>>> a.send([1, 'hello', None])725>>> b.recv()726[1, 'hello', None]727>>> b.send_bytes(b'thank you')728>>> a.recv_bytes()729b'thank you'730>>> import array731>>> arr1 = array.array('i', range(5))732>>> arr2 = array.array('i', [0] * 10)733>>> a.send_bytes(arr1)734>>> count = b.recv_bytes_into(arr2)735>>> assert count == len(arr1) * arr1.itemsize736>>> arr2737array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])738```739740> **Предупреждение**741>742> Метод [`Connection.recv()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection.recv) автоматически десериализует полученные данные, что может представлять угрозу безопасности, если нет доверия к процессу, отправившему сообщение.743>744> Поэтому, если объект соединения не был создан с помощью [`Pipe()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Pipe), методы [`recv()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection.recv) и [`send()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection.send) следует использовать только после выполнения некоторой аутентификации. См. [*Authentication keys*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-auth-keys).745746> **Предупреждение**747>748> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.749750### 16.3.2.5. Примитивы синхронизации751752Обычно примитивы синхронизации не так необходимы в многопроцессной программе, как в многопоточной. См. документацию модуля [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading).753754Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [*Менеджеры*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-managers).755756#### `class multiprocessing.BoundedSemaphore([value])`757758Объект ограниченного семафора: копия [`threading.BoundedSemaphore`](https://python-all.ru/3.1/library/threading.html#threading.BoundedSemaphore).759760(На Mac OS X этот объект неотличим от [`Semaphore`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализована на этой платформе).761762#### `class multiprocessing.Condition([lock])`763764Условная переменная: клон [`threading.Condition`](https://python-all.ru/3.1/library/threading.html#threading.Condition).765766If *lock* is specified then it should be a [`Lock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Lock) or [`RLock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.RLock) object from `multiprocessing`.767768#### `class multiprocessing.Event`769770Клон [`threading.Event`](https://python-all.ru/3.1/library/threading.html#threading.Event). Этот метод возвращает состояние внутреннего семафора при выходе, поэтому он всегда возвращает `True`, если только не задан тайм-аут и операция не истекает по времени.771772Изменено в версии 3.1: Ранее метод всегда возвращал `None`.773774#### `class multiprocessing.Lock`775776Объект нерекурсивной блокировки: копия777778[`threading.Lock`](https://python-all.ru/3.1/library/threading.html#threading.Lock)779780.781782#### `class multiprocessing.RLock`783784Объект рекурсивной блокировки: копия785786[`threading.RLock`](https://python-all.ru/3.1/library/threading.html#threading.RLock)787788.789790#### `class multiprocessing.Semaphore([value])`791792Объект семафора: копия793794[`threading.Semaphore`](https://python-all.ru/3.1/library/threading.html#threading.Semaphore)795796.797798> **Примечание**799>800> Метод `acquire()` объектов [`BoundedSemaphore`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Lock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.RLock) и [`Semaphore`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Semaphore) имеет параметр timeout, который не поддерживается аналогичными объектами в модуле [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading). Сигнатура: `acquire(block=True, timeout=None)`, при этом допускается передача именованных аргументов. Если *block* равен `True`, а *timeout* не равен `None`, то параметр задает тайм-аут в секундах. Если *block* равен `False`, то *timeout* игнорируется.801>802> В Mac OS X функция `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с таймаутом будет эмулировать её поведение с помощью цикла ожидания.803804> **Примечание**805>806> Если сигнал SIGINT, сгенерированный Ctrl-C, поступает в то время, как главный поток заблокирован вызовом `BoundedSemaphore.acquire()`, `Lock.acquire()`, `RLock.acquire()`, `Semaphore.acquire()`, `Condition.acquire()` или `Condition.wait()`, то вызов будет немедленно прерван и будет возбуждено [`KeyboardInterrupt`](https://python-all.ru/3.1/library/exceptions.html#KeyboardInterrupt).807>808> Это отличается от поведения [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading), где SIGINT будет игнорироваться, пока выполняются эквивалентные блокирующие вызовы.809810### 16.3.2.6. Общие объекты [`ctypes`](https://python-all.ru/3.1/library/ctypes.html#module-ctypes)811812Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.813814#### `multiprocessing.Value(typecode_or_type, *args[, lock])`815816Возвращает объект [`ctypes`](https://python-all.ru/3.1/library/ctypes.html#module-ctypes), выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для этого объекта.817818*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.1/library/array.html#module-array). *\*args* передаётся конструктору типа.819820Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».821822Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.823824#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`825826Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.827828*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.1/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* является последовательностью, которая используется для инициализации массива, и её длина определяет длину массива.829830Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».831832Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.833834Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.1/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.835836#### 16.3.2.6.1. Модуль `multiprocessing.sharedctypes`837838Модуль `multiprocessing.sharedctypes` предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/3.1/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.839840> **Примечание**841>842> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.843844#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`845846Возвращает массив ctypes, выделенный из разделяемой памяти.847848*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.1/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно задаёт длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.849850Обратите внимание, что запись и чтение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.851852#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`853854Возвращает объект ctypes, выделенный из разделяемой памяти.855856*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.1/library/array.html#module-array). *\*args* передаётся конструктору типа.857858Обратите внимание, что запись и чтение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.859860Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.1/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют хранить и извлекать строки – см. документацию по [`ctypes`](https://python-all.ru/3.1/library/ctypes.html#module-ctypes).861862#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *args[, lock])`863864То же, что и [`RawArray()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного массива ctypes.865866Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом `Lock` или `RLock`, то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».867868Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.869870#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args[, lock])`871872То же, что и [`RawValue()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного объекта ctypes.873874Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом `Lock` или `RLock`, то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».875876Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.877878#### `multiprocessing.sharedctypes.copy(obj)`879880Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes881882*obj*883884.885886#### `multiprocessing.sharedctypes.synchronized(obj[, lock])`887888Возвращает процесс-безопасный объект-обёртку для объекта ctypes, который использует *блокировка* для синхронизации доступа. Если *блокировка* равно `None` (по умолчанию), то автоматически создаётся объект [`multiprocessing.RLock`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.RLock).889890Синхронизированная обёртка будет иметь два дополнительных метода по сравнению с объектом, который она оборачивает: `get_obj()` возвращает обёрнутый объект, а `get_lock()` возвращает объект блокировки, используемый для синхронизации.891892Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.893894В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некий подкласс [`ctypes.Structure`](https://python-all.ru/3.1/library/ctypes.html#ctypes.Structure).)895896| ctypes | sharedctypes с типом | sharedctypes с typecode |897| --- | --- | --- |898| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |899| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) | |900| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |901| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |902903Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:904905```python906from multiprocessing import Process, Lock907from multiprocessing.sharedctypes import Value, Array908from ctypes import Structure, c_double909910class Point(Structure):911 _fields_ = [('x', c_double), ('y', c_double)]912913def modify(n, x, s, A):914 n.value **= 2915 x.value **= 2916 s.value = s.value.upper()917 for a in A:918 a.x **= 2919 a.y **= 2920921if __name__ == '__main__':922 lock = Lock()923924 n = Value('i', 7)925 x = Value(c_double, 1.0/3.0, lock=False)926 s = Array('c', 'hello world', lock=lock)927 A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)928929 p = Process(target=modify, args=(n, x, s, A))930 p.start()931 p.join()932933 print(n.value)934 print(x.value)935 print(s.value)936 print([(a.x, a.y) for a in A])937```938939Выводятся следующие результаты:940941```text942499430.1111111111111111944HELLO WORLD945[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]946```947948### 16.3.2.7. Менеджеры949950Менеджеры позволяют создавать данные, которыми могут обмениваться разные процессы. Объект менеджера управляет серверным процессом, который ведает *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.951952#### `multiprocessing.Manager()`953954Возвращает запущенный объект955956[`SyncManager`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.SyncManager)957958, который можно использовать для обмена объектами между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.959960Процессы менеджера завершаются сразу после сборки мусора или завершения родительского процесса. Классы менеджеров определены в модуле `multiprocessing.managers`:961962#### `class multiprocessing.managers.BaseManager([address[, authkey]])`963964Создает объект BaseManager.965966После создания следует вызвать [`start()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или [`serve_forever()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager.serve_forever), чтобы убедиться, что объект менеджера ссылается на запущенный процесс менеджера.967968*address* – это адрес, по которому процесс менеджера ожидает новые подключения. Если *address* равен `None`, то выбирается произвольный.969970*authkey* – это ключ аутентификации, который используется для проверки подлинности входящих подключений к серверному процессу. Если *authkey* равен `None`, то используется `current_process().authkey`. В противном случае используется *authkey*, который должен быть строкой.971972#### `start([initializer[, initargs]])`973974Запускает подпроцесс для запуска менеджера. Если975976*initializer*977978не равен979980`None`981982, то подпроцесс вызовет983984`initializer(*initargs)`985986при запуске.987988#### `serve_forever()`989990Запускает сервер в текущем процессе.991992#### `get_server()`993994Возвращает объект `Server`, который представляет фактический сервер под управлением Manager. Объект `Server` поддерживает метод [`serve_forever()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager.serve_forever):995996```python997>>> from multiprocessing.managers import BaseManager998>>> manager = BaseManager(address=('', 50000), authkey='abc')999>>> server = manager.get_server()1000>>> server.serve_forever()1001```10021003`Server` также имеет атрибут [`address`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).10041005#### `connect()`10061007Подключает локальный объект менеджера к удаленному процессу менеджера:10081009```python1010>>> from multiprocessing.managers import BaseManager1011>>> m = BaseManager(address=('127.0.0.1', 5000), authkey='abc')1012>>> m.connect()1013```10141015#### `shutdown()`10161017Останавливает процесс, используемый менеджером. Это доступно только в том случае, если [`start()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.10181019Этот метод можно вызывать несколько раз.10201021#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`10221023Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.10241025*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.10261027*callable* – это вызываемый объект, используемый для создания объектов для этого идентификатора типа. Если экземпляр менеджера будет создан с помощью метода класса `from_address()` или если аргумент *create\_method* равен `False`, то этот параметр можно оставить равным `None`.10281029*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.10301031*exposed* используется для указания последовательности имён методов, к которым прокси для этого typeid должны иметь доступ через `BaseProxy._callMethod()`. (Если *exposed* равен `None`, то используется `proxytype._exposed_`, если он существует.) В случае, когда список exposed не указан, все «открытые методы» разделяемого объекта будут доступны. (Здесь «открытый метод» означает любой атрибут, который имеет метод [`__call__()`](https://python-all.ru/3.1/reference/datamodel.html#object.__call__) и чьё имя не начинается с `'_'`.)10321033*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно сопоставляет имена методов со строками typeid. (Если *method\_to\_typeid* равен `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом в этом отображении или если отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.10341035*create\_method* определяет, должен ли быть создан метод с именем *typeid*, который можно использовать, чтобы сообщить серверному процессу о создании нового разделяемого объекта и возврате прокси для него. По умолчанию это `True`.10361037Экземпляры [`BaseManager`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:10381039#### `address`10401041Адрес, используемый менеджером.10421043#### `class multiprocessing.managers.SyncManager`10441045Подкласс [`BaseManager`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.10461047Он также поддерживает создание разделяемых списков и словарей.10481049#### `BoundedSemaphore([value])`10501051Создаёт разделяемый объект10521053[`threading.BoundedSemaphore`](https://python-all.ru/3.1/library/threading.html#threading.BoundedSemaphore)10541055и возвращает прокси для него.10561057#### `Condition([lock])`10581059Создаёт разделяемый объект [`threading.Condition`](https://python-all.ru/3.1/library/threading.html#threading.Condition) и возвращает прокси для него.10601061Если *блокировка* указан, то он должен быть прокси для объекта [`threading.Lock`](https://python-all.ru/3.1/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/3.1/library/threading.html#threading.RLock).10621063#### `Event()`10641065Создаёт разделяемый объект10661067[`threading.Event`](https://python-all.ru/3.1/library/threading.html#threading.Event)10681069и возвращает для него прокси.10701071#### `Lock()`10721073Создаёт разделяемый объект10741075[`threading.Lock`](https://python-all.ru/3.1/library/threading.html#threading.Lock)10761077и возвращает для него прокси.10781079#### `Namespace()`10801081Создаёт разделяемый объект10821083[`Namespace`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.SyncManager.Namespace)10841085и возвращает для него прокси.10861087#### `Queue([maxsize])`10881089Создаёт разделяемый объект10901091[`queue.Queue`](https://python-all.ru/3.1/library/queue.html#queue.Queue)10921093и возвращает для него прокси.10941095#### `RLock()`10961097Создаёт разделяемый объект10981099[`threading.RLock`](https://python-all.ru/3.1/library/threading.html#threading.RLock)11001101и возвращает для него прокси.11021103#### `Semaphore([value])`11041105Создаёт разделяемый объект11061107[`threading.Semaphore`](https://python-all.ru/3.1/library/threading.html#threading.Semaphore)11081109и возвращает прокси для него.11101111#### `Array(typecode, sequence)`11121113Создать массив и вернуть для него прокси.11141115#### `Value(typecode, value)`11161117Создаёт объект с атрибутом11181119`value`11201121, доступным для записи, и возвращает прокси для него.11221123#### `dict()`11241125#### `dict(mapping)`11261127#### `dict(sequence)`11281129Создаёт разделяемый объект11301131`dict`11321133и возвращает для него прокси.11341135#### `list()`11361137#### `list(sequence)`11381139Создаёт разделяемый объект11401141`list`11421143и возвращает для него прокси.11441145> **Примечание**1146>1147> Изменения изменяемых значений или элементов в прокси-объектах словарей и списков не будут распространяться через менеджер, поскольку прокси не может отследить, когда его значения или элементы изменяются. Чтобы изменить такой элемент, можно переназначить изменённый объект обратно в прокси-контейнер:1148>1149> ```python1150> # создать прокси для списка и добавить изменяемый объект (словарь)1151> lproxy = manager.list()1152> lproxy.append({})1153> # теперь изменить словарь1154> d = lproxy[0]1155> d['a'] = 11156> d['b'] = 21157> # на данный момент изменения в d еще не синхронизированы, но при1158> # при переприсваивании словаря прокси получает уведомление об изменении1159> lproxy[0] = d1160> ```11611162#### 16.3.2.7.1. Объекты пространства имён11631164Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.11651166Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:11671168```python1169>>> manager = multiprocessing.Manager()1170>>> Global = manager.Namespace()1171>>> Global.x = 101172>>> Global.y = 'hello'1173>>> Global._z = 12.3 # это атрибут прокси1174>>> print(Global)1175Namespace(x=10, y='hello')1176```11771178#### 16.3.2.7.2. Настраиваемые менеджеры11791180Чтобы создать собственный менеджер, нужно создать подкласс [`BaseManager`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager) и использовать метод класса [`register()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:11811182```python1183from multiprocessing.managers import BaseManager11841185class MathsClass:1186 def add(self, x, y):1187 return x + y1188 def mul(self, x, y):1189 return x * y11901191class MyManager(BaseManager):1192 pass11931194MyManager.register('Maths', MathsClass)11951196if __name__ == '__main__':1197 manager = MyManager()1198 manager.start()1199 maths = manager.Maths()1200 print(maths.add(4, 3)) # выводит 71201 print(maths.mul(7, 8)) # выводит 561202```12031204#### 16.3.2.7.3. Использование удалённого менеджера12051206Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).12071208Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:12091210```python1211>>> from multiprocessing.managers import BaseManager1212>>> import queue1213>>> queue = queue.Queue()1214>>> class QueueManager(BaseManager): pass1215>>> QueueManager.register('get_queue', callable=lambda:queue)1216>>> m = QueueManager(address=('', 50000), authkey='abracadabra')1217>>> s = m.get_server()1218>>> s.serve_forever()1219```12201221Один клиент может получить доступ к серверу следующим образом:12221223```python1224>>> from multiprocessing.managers import BaseManager1225>>> class QueueManager(BaseManager): pass1226>>> QueueManager.register('get_queue')1227>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')1228>>> m.connect()1229>>> queue = m.get_queue()1230>>> queue.put('hello')1231```12321233Другой клиент также может использовать его:12341235```python1236>>> from multiprocessing.managers import BaseManager1237>>> class QueueManager(BaseManager): pass1238>>> QueueManager.register('get_queue')1239>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')1240>>> m.connect()1241>>> queue = m.get_queue()1242>>> queue.get()1243'hello'1244```12451246Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:12471248```python1249>>> from multiprocessing import Process, Queue1250>>> from multiprocessing.managers import BaseManager1251>>> class Worker(Process):1252... def __init__(self, q):1253... self.q = q1254... super(Worker, self).__init__()1255... def run(self):1256... self.q.put('local hello')1257...1258>>> queue = Queue()1259>>> w = Worker(queue)1260>>> w.start()1261>>> class QueueManager(BaseManager): pass1262...1263>>> QueueManager.register('get_queue', callable=lambda: queue)1264>>> m = QueueManager(address=('', 50000), authkey='abracadabra')1265>>> s = m.get_server()1266>>> s.serve_forever()1267```12681269### 16.3.2.8. Прокси-объекты12701271Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.12721273Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:12741275```python1276>>> from multiprocessing import Manager1277>>> manager = Manager()1278>>> l = manager.list([i*i for i in range(10)])1279>>> print(l)1280[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1281>>> print(repr(l))1282<ListProxy object, typeid 'list' at 0x...>1283>>> l[4]1284161285>>> l[2:5]1286[4, 9, 16]1287```12881289Обратите внимание, что применение [`str()`](https://python-all.ru/3.1/library/functions.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/3.1/library/functions.html#repr) вернёт представление прокси.12901291Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:12921293```python1294>>> a = manager.list()1295>>> b = manager.list()1296>>> a.append(b) # референт a теперь содержит референт b1297>>> print(a, b)1298[[]] []1299>>> b.append('hello')1300>>> print(a, b)1301[['hello']] ['hello']1302```13031304> **Примечание**1305>1306> Типы прокси в `multiprocessing` не поддерживают сравнение по значению. Так, например:1307>1308> ```python1309> >>> manager.list([1,2,3]) == [1,2,3]1310> False1311> ```1312>1313> При сравнении следует просто использовать копию референта.13141315#### `class multiprocessing.managers.BaseProxy`13161317Объекты прокси являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseProxy).13181319#### `_callmethod(methodname[, args[, kwds]])`13201321Вызывает метод референта прокси и возвращает результат.13221323Если `proxy` является прокси, чей референт – `obj`, то выражение13241325```python1326proxy._callmethod(methodname, args, kwds)1327```13281329вычислит выражение13301331```python1332getattr(obj, methodname)(*args, **kwds)1333```13341335в процессе менеджера.13361337Возвращаемое значение будет копией результата вызова или прокси на новый разделяемый объект – см. документацию по аргументу *method\_to\_typeid* метода [`BaseManager.register()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).13381339Если вызов вызывает исключение, то оно повторно возбуждается методом [`_callmethod()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). Если в процессе менеджера возникает другое исключение, оно преобразуется в исключение `RemoteError` и возбуждается методом [`_callmethod()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod).13401341Обратите внимание: если *methodname* не был *опубликован*, будет возбуждено исключение.13421343Пример использования [`_callmethod()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):13441345```python1346>>> l = manager.list(range(10))1347>>> l._callmethod('__len__')1348101349>>> l._callmethod('__getslice__', (2, 7)) # эквивалентно `l[2:7]`1350[2, 3, 4, 5, 6]1351>>> l._callmethod('__getitem__', (20,)) # эквивалентно `l[20]`1352Traceback (most recent call last):1353...1354IndexError: list index out of range1355```13561357#### `_getvalue()`13581359Возвращает копию референта.13601361Если референт не может быть сериализован (unpicklable), то это вызовет исключение.13621363#### `__repr__()`13641365Возвращает представление прокси-объекта.13661367#### `__str__()`13681369Возвращает представление референта.13701371#### 16.3.2.8.1. Очистка13721373Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.13741375Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.13761377### 16.3.2.9. Пулы процессов13781379Можно создать пул процессов, которые будут выполнять отправленные им задачи с помощью класса `Pool`.13801381#### `class multiprocessing.Pool([processes[, initializer[, initargs]]])`13821383Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.13841385*processes* – количество используемых рабочих процессов. Если *processes* равно `None`, используется число, возвращаемое `cpu_count()`. Если *initializer* не `None`, каждый рабочий процесс при запуске вызовет `initializer(*initargs)`.13861387#### `apply(func[, args[, kwds]])`13881389Вызывает13901391*func*13921393с аргументами13941395*args*13961397и ключевыми аргументами13981399*kwds*14001401. Блокируется до тех пор, пока результат не будет готов. Поскольку этот метод блокируется,14021403[`apply_async()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.apply_async)14041405лучше подходит для параллельного выполнения работы. Кроме того, переданная функция выполняется только одним из рабочих процессов пула.14061407#### `apply_async(func[, args[, kwds[, callback]]])`14081409Вариант метода [`apply()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.apply), который возвращает объект результата.14101411Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.14121413#### `map(func, iterable[, chunksize])`14141415Параллельный аналог встроенной функции [`map()`](https://python-all.ru/3.1/library/functions.html#map) (однако поддерживает только один *итерируемый объект*). Блокируется до готовности результата.14161417Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.14181419#### `map_async(func, iterable[, chunksize[, callback]])`14201421Вариант метода [`map()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map), который возвращает объект результата.14221423Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.14241425#### `imap(func, iterable[, chunksize])`14261427Более ленивая версия [`map()`](https://python-all.ru/3.1/library/functions.html#map).14281429Аргумент *chunksize* аналогичен тому, что используется в методе [`map()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map). Для очень длинных итерируемых объектов использование большого значения *chunksize* может сделать выполнение задачи **значительно** быстрее, чем использование значения по умолчанию `1`.14301431Кроме того, если *chunksize* равно `1`, то метод `next()` итератора, возвращаемого методом [`imap()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` возбудит `multiprocessing.TimeoutError`, если результат не может быть возвращён в течение *timeout* секунд.14321433#### `imap_unordered(func, iterable[, chunksize])`14341435То же, что и14361437[`imap()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap)14381439, но порядок результатов из возвращаемого итератора может быть произвольным. (Только при наличии одного рабочего процесса порядок гарантированно «правильный».)14401441#### `close()`14421443Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.14441445#### `terminate()`14461447Немедленно останавливает рабочие процессы, не завершая незавершённую работу. Когда объект пула собирается сборщиком мусора,14481449[`terminate()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate)14501451будет вызван немедленно.14521453#### `join()`14541455Ожидает завершения рабочих процессов. Перед вызовом14561457[`join()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.join)14581459необходимо вызвать14601461[`close()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.close)14621463или14641465[`terminate()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate)14661467.14681469#### `class multiprocessing.pool.AsyncResult`14701471Класс результата, возвращаемого методами `Pool.apply_async()` и `Pool.map_async()`.14721473#### `get([timeout])`14741475Возвращает результат, когда он будет получен. Если14761477*timeout*14781479не равно14801481`None`14821483и результат не получен в течение14841485*timeout*14861487секунд, то возбуждается14881489`multiprocessing.TimeoutError`14901491. Если удалённый вызов возбудил исключение, то это исключение будет повторно возбуждено методом14921493[`get()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get)14941495.14961497#### `wait([timeout])`14981499Ожидает, пока результат не станет доступен или пока не пройдёт15001501*timeout*15021503секунд.15041505#### `ready()`15061507Возвращает, завершён ли вызов.15081509#### `successful()`15101511Возвращает, завершился ли вызов без возбуждения исключения. Возбуждает15121513[`AssertionError`](https://python-all.ru/3.1/library/exceptions.html#AssertionError)15141515, если результат ещё не готов.15161517Следующий пример демонстрирует использование пула:15181519```python1520from multiprocessing import Pool15211522def f(x):1523 return x*x15241525if __name__ == '__main__':1526 pool = Pool(processes=4) # запустить 4 рабочих процесса15271528 result = pool.apply_async(f, (10,)) # асинхронно вычислить "f(10)"1529 print(result.get(timeout=1)) # выводит "100", если компьютер не *очень* медленный15301531 print(pool.map(f, range(10))) # выводит "[0, 1, 4,..., 81]"15321533 it = pool.imap(f, range(10))1534 print(next(it)) # выводит "0"1535 print(next(it)) # выводит "1"1536 print(it.next(timeout=1)) # выводит "4", если компьютер не *очень* медленный15371538 import time1539 result = pool.apply_async(time.sleep, (10,))1540 print(result.get(timeout=1)) # вызывает TimeoutError1541```15421543### 16.3.2.10. Прослушиватели и клиенты15441545Обычно обмен сообщениями между процессами осуществляется с помощью очередей или с использованием объектов `Connection`, возвращаемых `Pipe()`.15461547Однако модуль `multiprocessing.connection` предоставляет дополнительную гибкость. По сути, он предлагает высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows, а также поддерживает *аутентификацию с помощью дайджеста* с использованием модуля [`hmac`](https://python-all.ru/3.1/library/hmac.html#module-hmac).15481549#### `multiprocessing.connection.deliver_challenge(connection, authkey)`15501551Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.15521553Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).15541555#### `multiprocessing.connection.answerChallenge(connection, authkey)`15561557Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.15581559Если приветственное сообщение не получено, то возникает исключение [`AuthenticationError`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).15601561#### `multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`15621563Пытается установить соединение с прослушивателем, который использует адрес *address*, и возвращает объект [`Connection`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Connection).15641565Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [*Address Formats*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-address-formats))15661567Если *authenticate* равно `True` или *authkey* – строка, то используется аутентификация с помощью дайджеста. Ключом для аутентификации будет либо *authkey*, либо `current_process().authkey)`, если *authkey* равно `None`. Если аутентификация не удается, возникает исключение [`AuthenticationError`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.connection.AuthenticationError). См. [*Authentication keys*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-auth-keys).15681569#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`15701571Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.15721573*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.15741575> **Примечание**1576>1577> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.15781579*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них только первый гарантированно доступен. Если *family* равен `None`, то семейство определяется по формату *address*. Если *address* также равен `None`, то выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [*Address Formats*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равен `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/3.1/library/tempfile.html#tempfile.mkstemp).15801581Если объект прослушивателя использует сокет, то *backlog* (по умолчанию 1) передаётся методу `listen()` сокета после его привязки.15821583Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.15841585Если *authkey* – строка, то она будет использоваться как ключ аутентификации; в противном случае должно быть *None*.15861587Если *authkey* равно `None` и *authenticate* равно `True`, то в качестве ключа аутентификации используется `current_process().authkey`. Если *authkey* равно `None` и *authenticate* равно `False`, то аутентификация не производится. Если аутентификация не удалась, то возникает исключение [`AuthenticationError`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.connection.AuthenticationError). См. [*Ключи аутентификации*](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing-auth-keys).15881589#### `accept()`15901591Принимает соединение на привязанном сокете или именованном канале объекта прослушивателя и возвращает объект15921593`Connection`15941595. Если аутентификация запрошена и не удалась, то возникает исключение15961597[`AuthenticationError`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.connection.AuthenticationError)15981599.16001601#### `close()`16021603Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.16041605Объекты Listener имеют следующие свойства только для чтения:16061607#### `address`16081609Адрес, используемый объектом Listener.16101611#### `last_accepted`16121613Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то16141615`None`16161617.16181619Модуль определяет два исключения:16201621#### `exception multiprocessing.connection.AuthenticationError`16221623Исключение, возникающее при ошибке аутентификации.16241625**Примеры**16261627Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:16281629```python1630from multiprocessing.connection import Listener1631from array import array16321633address = ('localhost', 6000) # семейство определяется как 'AF_INET'1634listener = Listener(address, authkey=b'secret password')16351636conn = listener.accept()1637print('connection accepted from', listener.last_accepted)16381639conn.send([2.25, None, 'junk', float])16401641conn.send_bytes(b'hello')16421643conn.send_bytes(array('i', [42, 1729]))16441645conn.close()1646listener.close()1647```16481649Следующий код подключается к серверу и получает от него некоторые данные:16501651```python1652from multiprocessing.connection import Client1653from array import array16541655address = ('localhost', 6000)1656conn = Client(address, authkey=b'secret password')16571658print(conn.recv()) # => [2.25, None, 'junk', float]16591660print(conn.recv_bytes()) # => 'hello'16611662arr = array('i', [0, 0, 0, 0, 0])1663print(conn.recv_bytes_into(arr)) # => 81664print(arr) # => array('i', [42, 1729, 0, 0, 0])16651666conn.close()1667```16681669#### 16.3.2.10.1. Форматы адресов16701671- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.1672- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.1673- **Адрес `'AF_PIPE'` – это строка вида**16741675 `r'\\.\pipe\PipeName'`. Чтобы использовать [`Client()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.connection.Client) для подключения к именованному каналу на удалённом компьютере с именем *ServerName*, следует использовать адрес вида `r'\\ServerName\pipe\PipeName'` вместо.16761677Обратите внимание, что любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не адресом `'AF_UNIX'`.16781679### 16.3.2.11. Ключи аутентификации16801681При использовании `Connection.recv()` полученные данные автоматически распаковываются. К сожалению, распаковка данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/3.1/library/hmac.html#module-hmac) для аутентификации по дайджесту.16821683Ключ аутентификации – это строка, которую можно рассматривать как пароль: как только соединение установлено, обе стороны требуют подтверждения, что другая сторона знает ключ аутентификации. (Доказательство того, что обе стороны используют один и тот же ключ, **не** подразумевает отправку ключа по соединению.)16841685Если запрошена аутентификация, но ключ аутентификации не указан, используется возвращаемое значение `current_process().authkey` (см. [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process)). Это значение автоматически наследуется любым объектом [`Process`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.Process), который создает текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы будут использовать один ключ аутентификации, который можно использовать при настройке соединений между ними.16861687Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/3.1/library/os.html#os.urandom).16881689### 16.3.2.12. Журналирование16901691Доступна некоторая поддержка логирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/3.1/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому возможно (в зависимости от типа обработчика) перемешивание сообщений от разных процессов.16921693#### `multiprocessing.get_logger()`16941695Возвращает регистратор (logger), используемый модулем `multiprocessing`. При необходимости будет создан новый.16961697При первом создании регистратор имеет уровень `logging.NOTSET` и не имеет обработчика по умолчанию. Сообщения, отправленные этому регистратору, по умолчанию не распространяются на корневой регистратор.16981699Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.17001701#### `multiprocessing.log_to_stderr()`17021703Эта функция вызывает17041705[`get_logger()`](https://python-all.ru/3.1/library/multiprocessing.html#multiprocessing.get_logger)17061707, но в дополнение к возврату регистратора, созданного get\_logger, она добавляет обработчик, который отправляет вывод в17081709[`sys.stderr`](https://python-all.ru/3.1/library/sys.html#sys.stderr)17101711, используя формат17121713`'[%(levelname)s/%(processName)s] %(message)s'`17141715.17161717Ниже приведён пример сеанса с включённым журналированием:17181719```python1720>>> import multiprocessing, logging1721>>> logger = multiprocessing.log_to_stderr()1722>>> logger.setLevel(logging.INFO)1723>>> logger.warning('doomed')1724[WARNING/MainProcess] doomed1725>>> m = multiprocessing.Manager()1726[INFO/SyncManager-...] child process calling self.run()1727[INFO/SyncManager-...] created temp directory /.../pymp-...1728[INFO/SyncManager-...] manager serving at '/.../listener-...'1729>>> del m1730[INFO/MainProcess] sending shutdown message to manager1731[INFO/SyncManager-...] manager exiting with exitcode 01732```17331734В дополнение к этим двум функциям ведения журнала, multiprocessing также предоставляет два дополнительных атрибута уровня журналирования. Это `SUBWARNING` и `SUBDEBUG`. Приведенная ниже таблица показывает, где они располагаются в обычной иерархии уровней.17351736| Уровень | Числовое значение |1737| --- | --- |1738| `SUBWARNING` | 25 |1739| `SUBDEBUG` | 5 |17401741Полную таблицу уровней логирования см. в модуле [`logging`](https://python-all.ru/3.1/library/logging.html#module-logging).17421743Эти дополнительные уровни журналирования используются в основном для некоторых отладочных сообщений в модуле multiprocessing. Ниже приведен тот же пример, что и выше, но с включенным `SUBDEBUG`:17441745```python1746>>> import multiprocessing, logging1747>>> logger = multiprocessing.log_to_stderr()1748>>> logger.setLevel(multiprocessing.SUBDEBUG)1749>>> logger.warning('doomed')1750[WARNING/MainProcess] doomed1751>>> m = multiprocessing.Manager()1752[INFO/SyncManager-...] child process calling self.run()1753[INFO/SyncManager-...] created temp directory /.../pymp-...1754[INFO/SyncManager-...] manager serving at '/.../pymp-djGBXN/listener-...'1755>>> del m1756[SUBDEBUG/MainProcess] finalizer calling ...1757[INFO/MainProcess] sending shutdown message to manager1758[DEBUG/SyncManager-...] manager received shutdown message1759[SUBDEBUG/SyncManager-...] calling <Finalize object, callback=unlink, ...1760[SUBDEBUG/SyncManager-...] finalizer calling <built-in function unlink> ...1761[SUBDEBUG/SyncManager-...] calling <Finalize object, dead>1762[SUBDEBUG/SyncManager-...] finalizer calling <function rmtree at 0x5aa730> ...1763[INFO/SyncManager-...] manager exiting with exitcode 01764```17651766### 16.3.2.13. Модуль `multiprocessing.dummy`17671768`multiprocessing.dummy` повторяет API модуля `multiprocessing`, но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading).17691770## 16.3.3. Рекомендации по программированию17711772При использовании `multiprocessing` следует придерживаться определённых правил и идиом.17731774### 16.3.3.1. Все платформы17751776Избегайте разделяемого состояния17771778> По возможности следует стараться избегать передачи больших объёмов данных между процессами.1779>1780> Вероятно, лучше использовать очереди или каналы для связи между процессами, чем низкоуровневые примитивы синхронизации из модуля [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading).17811782Сериализуемость17831784> Убедитесь, что аргументы методов прокси-объектов сериализуемы.17851786Потокобезопасность прокси17871788> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.1789>1790> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)17911792Присоединение процессов-зомби17931794> В Unix, когда процесс завершается, но к нему не был присоединён вызовом join, он становится зомби. Их не должно быть много, потому что каждый раз при запуске нового процесса (или вызове1795>1796> `active_children()`1797>1798> ) все завершённые процессы, к которым ещё не был применён join, будут присоединены. Также вызов1799>1800> `Process.is_alive()`1801>1802> для завершённого процесса присоединит его. Тем не менее, считается хорошей практикой явно присоединять все запущенные процессы.18031804Лучше наследовать, чем упаковывать/распаковывать18051806> В Windows многие типы из1807>1808> `multiprocessing`1809>1810> должны быть сериализуемыми (picklable), чтобы дочерние процессы могли их использовать. Однако, как правило, следует избегать отправки разделяемых объектов другим процессам через каналы (pipes) или очереди (queues). Вместо этого программу следует организовать так, чтобы процесс, которому нужен доступ к разделяемому ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.18111812Избегайте завершения процессов18131814> Использование метода `Process.terminate()` для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), используемые в данный момент процессом, станут повреждёнными или недоступными для других процессов.1815>1816> Поэтому, вероятно, лучше рассматривать использование `Process.terminate()` только для процессов, которые никогда не используют разделяемые ресурсы.18171818Присоединение процессов, использующих очереди18191820> Имейте в виду, что процесс, поместивший элементы в очередь, будет ждать перед завершением, пока все буферизованные элементы не будут переданы потоком «feeder» в нижележащий канал. (Дочерний процесс может вызвать метод очереди `Queue.cancel_join_thread()`, чтобы избежать такого поведения.)1821>1822> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещенные в очередь, в конечном итоге будут удалены до того, как процесс будет присоединен (joined). В противном случае нельзя быть уверенным, что процессы, которые помещали элементы в очередь, завершатся. Также помните, что недемонические процессы будут присоединены автоматически.1823>1824> Пример, который приведёт к взаимоблокировке:1825>1826> ```python1827> from multiprocessing import Process, Queue1828>1829> def f(q):1830> q.put('X' * 1000000)1831>1832> if __name__ == '__main__':1833> queue = Queue()1834> p = Process(target=f, args=(queue,))1835> p.start()1836> p.join() # это приводит к взаимоблокировке1837> obj = queue.get()1838> ```1839>1840> Исправлением здесь была бы перестановка двух последних строк (или просто удаление строки с `p.join()`).18411842Явная передача ресурсов дочерним процессам18431844> В Unix дочерний процесс может использовать общий ресурс, созданный в родительском процессе, с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.1845>1846> Помимо обеспечения (потенциальной) совместимости с Windows, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если какой-либо ресурс освобождается при сборке мусора в родительском процессе.1847>1848> Так, например1849>1850> ```python1851> from multiprocessing import Process, Lock1852>1853> def f():1854> ... do something using "lock" ...1855>1856> if __name__ == '__main__':1857> lock = Lock()1858> for i in range(10):1859> Process(target=f).start()1860> ```1861>1862> следует переписать как1863>1864> ```python1865> from multiprocessing import Process, Lock1866>1867> def f(l):1868> ... do something using "l" ...1869>1870> if __name__ == '__main__':1871> lock = Lock()1872> for i in range(10):1873> Process(target=f, args=(lock,)).start()1874> ```18751876Осторожно: замена sys.stdin на «файлоподобный объект»18771878> `multiprocessing` изначально безусловно вызывал:1879>1880> ```python1881> os.close(sys.stdin.fileno())1882> ```1883>1884> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:1885>1886> ```python1887> sys.stdin.close()1888> sys.stdin = open(os.devnull)1889> ```1890>1891> Это решает фундаментальную проблему конфликтов процессов, приводящих к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, заменяющих [`sys.stdin()`](https://python-all.ru/3.1/library/sys.html#sys.stdin) на «объект, подобный файлу» (file-like object) с буферизацией вывода. Опасность заключается в том, что если несколько процессов вызовут `close()` для этого объекта, одни и те же данные могут быть сброшены в объект несколько раз, что приведёт к повреждению данных.1892>1893> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:1894>1895> ```python1896> @property1897> def cache(self):1898> pid = os.getpid()1899> if pid != self._pid:1900> self._pid = pid1901> self._cache = []1902> return self._cache1903> ```1904>1905> Дополнительную информацию см. в [issue 5155](https://python-all.ru/3.1/library/multiprocessing.html), [issue 5313](https://python-all.ru/3.1/library/multiprocessing.html) и [issue 5331](https://python-all.ru/3.1/library/multiprocessing.html).19061907### 16.3.3.2. Windows19081909Поскольку в Windows нет [`os.fork()`](https://python-all.ru/3.1/library/os.html#os.fork), существуют несколько дополнительных ограничений:19101911Упаковываемость19121913> Убедитесь, что все аргументы `Process.__init__()` являются сериализуемыми (picklable). Это означает, в частности, что связанные или несвязанные методы нельзя использовать напрямую в качестве аргумента `target` в Windows – просто определите функцию и используйте ее вместо этого.1914>1915> Кроме того, если вы создаёте подкласс `Process`, убедитесь, что экземпляры будут сериализуемы с помощью pickle при вызове метода `Process.start()`.19161917Глобальные переменные19181919> Имейте в виду, что если код, выполняемый в дочернем процессе, пытается получить доступ к глобальной переменной, то значение, которое он видит (если оно есть), может не совпадать со значением в родительском процессе на момент вызова `Process.start()`.1920>1921> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.19221923Безопасный импорт главного модуля19241925> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).1926>1927> Например, в Windows запуск следующего модуля завершится ошибкой [`RuntimeError`](https://python-all.ru/3.1/library/exceptions.html#RuntimeError):1928>1929> ```python1930> from multiprocessing import Process1931>1932> def foo():1933> print('hello')1934>1935> p = Process(target=foo)1936> p.start()1937> ```1938>1939> Вместо этого следует защитить «точку входа» программы, используя конструкцию `if __name__ == '__main__':` следующим образом:1940>1941> ```python1942> from multiprocessing import Process, freeze_support1943>1944> def foo():1945> print('hello')1946>1947> if __name__ == '__main__':1948> freeze_support()1949> p = Process(target=foo)1950> p.start()1951> ```1952>1953> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не в замороженном виде.)1954>1955> Это позволяет новому порождённому интерпретатору Python безопасно импортировать модуль, а затем выполнить функцию `foo()` этого модуля.1956>1957> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.19581959## 16.3.4. Примеры19601961Демонстрация создания и использования настраиваемых менеджеров и прокси:19621963```python1964#1965# This module shows how to use arbitrary callables with a subclass of1966# `BaseManager`.1967#1968# Copyright (c) 2006-2008, R Oudkerk1969# All rights reserved.1970#19711972from multiprocessing import freeze_support1973from multiprocessing.managers import BaseManager, BaseProxy1974import operator19751976##19771978class Foo:1979 def f(self):1980 print('you called Foo.f()')1981 def g(self):1982 print('you called Foo.g()')1983 def _h(self):1984 print('you called Foo._h()')19851986# A simple generator function1987def baz():1988 for i in range(10):1989 yield i*i19901991# Proxy type for generator objects1992class GeneratorProxy(BaseProxy):1993 _exposed_ = ('next', '__next__')1994 def __iter__(self):1995 return self1996 def __next__(self):1997 return self._callmethod('next')1998 def __next__(self):1999 return self._callmethod('__next__')20002001# Function to return the operator module2002def get_operator_module():2003 return operator20042005##20062007class MyManager(BaseManager):2008 pass20092010# register the Foo class; make `f()` and `g()` accessible via proxy2011MyManager.register('Foo1', Foo)20122013# register the Foo class; make `g()` and `_h()` accessible via proxy2014MyManager.register('Foo2', Foo, exposed=('g', '_h'))20152016# register the generator function baz; use `GeneratorProxy` to make proxies2017MyManager.register('baz', baz, proxytype=GeneratorProxy)20182019# register get_operator_module(); make public functions accessible via proxy2020MyManager.register('operator', get_operator_module)20212022##20232024def test():2025 manager = MyManager()2026 manager.start()20272028 print('-' * 20)20292030 f1 = manager.Foo1()2031 f1.f()2032 f1.g()2033 assert not hasattr(f1, '_h')2034 assert sorted(f1._exposed_) == sorted(['f', 'g'])20352036 print('-' * 20)20372038 f2 = manager.Foo2()2039 f2.g()2040 f2._h()2041 assert not hasattr(f2, 'f')2042 assert sorted(f2._exposed_) == sorted(['g', '_h'])20432044 print('-' * 20)20452046 it = manager.baz()2047 for i in it:2048 print('<%d>' % i, end=' ')2049 print()20502051 print('-' * 20)20522053 op = manager.operator()2054 print('op.add(23, 45) =', op.add(23, 45))2055 print('op.pow(2, 94) =', op.pow(2, 94))2056 print('op.getslice(range(10), 2, 6) =', op.getslice(list(range(10)), 2, 6))2057 print('op.repeat(range(5), 3) =', op.repeat(list(range(5)), 3))2058 print('op._exposed_ =', op._exposed_)20592060##20612062if __name__ == '__main__':2063 freeze_support()2064 test()2065```20662067Использование `Pool`:20682069```python2070#2071# A test of `multiprocessing.Pool` class2072#2073# Copyright (c) 2006-2008, R Oudkerk2074# All rights reserved.2075#20762077import multiprocessing2078import time2079import random2080import sys20812082#2083# Functions used by test code2084#20852086def calculate(func, args):2087 result = func(*args)2088 return '%s says that %s%s = %s' % (2089 multiprocessing.current_process().name,2090 func.__name__, args, result2091 )20922093def calculatestar(args):2094 return calculate(*args)20952096def mul(a, b):2097 time.sleep(0.5*random.random())2098 return a * b20992100def plus(a, b):2101 time.sleep(0.5*random.random())2102 return a + b21032104def f(x):2105 return 1.0 / (x-5.0)21062107def pow3(x):2108 return x**321092110def noop(x):2111 pass21122113#2114# Test code2115#21162117def test():2118 print('cpu_count() = %d\n' % multiprocessing.cpu_count())21192120 #2121 # Create pool2122 #21232124 PROCESSES = 42125 print('Creating pool with %d processes\n' % PROCESSES)2126 pool = multiprocessing.Pool(PROCESSES)2127 print('pool = %s' % pool)2128 print()21292130 #2131 # Tests2132 #21332134 TASKS = [(mul, (i, 7)) for i in range(10)] + \2135 [(plus, (i, 8)) for i in range(10)]21362137 results = [pool.apply_async(calculate, t) for t in TASKS]2138 imap_it = pool.imap(calculatestar, TASKS)2139 imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)21402141 print('Ordered results using pool.apply_async():')2142 for r in results:2143 print('\t', r.get())2144 print()21452146 print('Ordered results using pool.imap():')2147 for x in imap_it:2148 print('\t', x)2149 print()21502151 print('Unordered results using pool.imap_unordered():')2152 for x in imap_unordered_it:2153 print('\t', x)2154 print()21552156 print('Ordered results using pool.map() --- will block till complete:')2157 for x in pool.map(calculatestar, TASKS):2158 print('\t', x)2159 print()21602161 #2162 # Simple benchmarks2163 #21642165 N = 1000002166 print('def pow3(x): return x**3')21672168 t = time.time()2169 A = list(map(pow3, range(N)))2170 print('\tmap(pow3, range(%d)):\n\t\t%s seconds' % \2171 (N, time.time() - t))21722173 t = time.time()2174 B = pool.map(pow3, range(N))2175 print('\tpool.map(pow3, range(%d)):\n\t\t%s seconds' % \2176 (N, time.time() - t))21772178 t = time.time()2179 C = list(pool.imap(pow3, range(N), chunksize=N//8))2180 print('\tlist(pool.imap(pow3, range(%d), chunksize=%d)):\n\t\t%s' \2181 ' seconds' % (N, N//8, time.time() - t))21822183 assert A == B == C, (len(A), len(B), len(C))2184 print()21852186 L = [None] * 10000002187 print('def noop(x): pass')2188 print('L = [None] * 1000000')21892190 t = time.time()2191 A = list(map(noop, L))2192 print('\tmap(noop, L):\n\t\t%s seconds' % \2193 (time.time() - t))21942195 t = time.time()2196 B = pool.map(noop, L)2197 print('\tpool.map(noop, L):\n\t\t%s seconds' % \2198 (time.time() - t))21992200 t = time.time()2201 C = list(pool.imap(noop, L, chunksize=len(L)//8))2202 print('\tlist(pool.imap(noop, L, chunksize=%d)):\n\t\t%s seconds' % \2203 (len(L)//8, time.time() - t))22042205 assert A == B == C, (len(A), len(B), len(C))2206 print()22072208 del A, B, C, L22092210 #2211 # Test error handling2212 #22132214 print('Testing error handling:')22152216 try:2217 print(pool.apply(f, (5,)))2218 except ZeroDivisionError:2219 print('\tGot ZeroDivisionError as expected from pool.apply()')2220 else:2221 raise AssertionError('expected ZeroDivisionError')22222223 try:2224 print(pool.map(f, list(range(10))))2225 except ZeroDivisionError:2226 print('\tGot ZeroDivisionError as expected from pool.map()')2227 else:2228 raise AssertionError('expected ZeroDivisionError')22292230 try:2231 print(list(pool.imap(f, list(range(10)))))2232 except ZeroDivisionError:2233 print('\tGot ZeroDivisionError as expected from list(pool.imap())')2234 else:2235 raise AssertionError('expected ZeroDivisionError')22362237 it = pool.imap(f, list(range(10)))2238 for i in range(10):2239 try:2240 x = next(it)2241 except ZeroDivisionError:2242 if i == 5:2243 pass2244 except StopIteration:2245 break2246 else:2247 if i == 5:2248 raise AssertionError('expected ZeroDivisionError')22492250 assert i == 92251 print('\tGot ZeroDivisionError as expected from IMapIterator.next()')2252 print()22532254 #2255 # Testing timeouts2256 #22572258 print('Testing ApplyResult.get() with timeout:', end=' ')2259 res = pool.apply_async(calculate, TASKS[0])2260 while 1:2261 sys.stdout.flush()2262 try:2263 sys.stdout.write('\n\t%s' % res.get(0.02))2264 break2265 except multiprocessing.TimeoutError:2266 sys.stdout.write('.')2267 print()2268 print()22692270 print('Testing IMapIterator.next() with timeout:', end=' ')2271 it = pool.imap(calculatestar, TASKS)2272 while 1:2273 sys.stdout.flush()2274 try:2275 sys.stdout.write('\n\t%s' % it.next(0.02))2276 except StopIteration:2277 break2278 except multiprocessing.TimeoutError:2279 sys.stdout.write('.')2280 print()2281 print()22822283 #2284 # Testing callback2285 #22862287 print('Testing callback:')22882289 A = []2290 B = [56, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]22912292 r = pool.apply_async(mul, (7, 8), callback=A.append)2293 r.wait()22942295 r = pool.map_async(pow3, list(range(10)), callback=A.extend)2296 r.wait()22972298 if A == B:2299 print('\tcallbacks succeeded\n')2300 else:2301 print('\t*** callbacks failed\n\t\t%s != %s\n' % (A, B))23022303 #2304 # Check there are no outstanding tasks2305 #23062307 assert not pool._cache, 'cache = %r' % pool._cache23082309 #2310 # Check close() methods2311 #23122313 print('Testing close():')23142315 for worker in pool._pool:2316 assert worker.is_alive()23172318 result = pool.apply_async(time.sleep, [0.5])2319 pool.close()2320 pool.join()23212322 assert result.get() is None23232324 for worker in pool._pool:2325 assert not worker.is_alive()23262327 print('\tclose() succeeded\n')23282329 #2330 # Check terminate() method2331 #23322333 print('Testing terminate():')23342335 pool = multiprocessing.Pool(2)2336 DELTA = 0.12337 ignore = pool.apply(pow3, [2])2338 results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]2339 pool.terminate()2340 pool.join()23412342 for worker in pool._pool:2343 assert not worker.is_alive()23442345 print('\tterminate() succeeded\n')23462347 #2348 # Check garbage collection2349 #23502351 print('Testing garbage collection:')23522353 pool = multiprocessing.Pool(2)2354 DELTA = 0.12355 processes = pool._pool2356 ignore = pool.apply(pow3, [2])2357 results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]23582359 results = pool = None23602361 time.sleep(DELTA * 2)23622363 for worker in processes:2364 assert not worker.is_alive()23652366 print('\tgarbage collection succeeded\n')23672368if __name__ == '__main__':2369 multiprocessing.freeze_support()23702371 assert len(sys.argv) in (1, 2)23722373 if len(sys.argv) == 1 or sys.argv[1] == 'processes':2374 print(' Using processes '.center(79, '-'))2375 elif sys.argv[1] == 'threads':2376 print(' Using threads '.center(79, '-'))2377 import multiprocessing.dummy as multiprocessing2378 else:2379 print('Usage:\n\t%s [processes | threads]' % sys.argv[0])2380 raise SystemExit(2)23812382 test()2383```23842385Типы синхронизации, такие как блокировки, условия и очереди:23862387```python2388#2389# A test file for the `multiprocessing` package2390#2391# Copyright (c) 2006-2008, R Oudkerk2392# All rights reserved.2393#23942395import time, sys, random2396from queue import Empty23972398import multiprocessing # may get overwritten23992400#### TEST_VALUE24012402def value_func(running, mutex):2403 random.seed()2404 time.sleep(random.random()*4)24052406 mutex.acquire()2407 print('\n\t\t\t' + str(multiprocessing.current_process()) + ' has finished')2408 running.value -= 12409 mutex.release()24102411def test_value():2412 TASKS = 102413 running = multiprocessing.Value('i', TASKS)2414 mutex = multiprocessing.Lock()24152416 for i in range(TASKS):2417 p = multiprocessing.Process(target=value_func, args=(running, mutex))2418 p.start()24192420 while running.value > 0:2421 time.sleep(0.08)2422 mutex.acquire()2423 print(running.value, end=' ')2424 sys.stdout.flush()2425 mutex.release()24262427 print()2428 print('No more running processes')24292430#### TEST_QUEUE24312432def queue_func(queue):2433 for i in range(30):2434 time.sleep(0.5 * random.random())2435 queue.put(i*i)2436 queue.put('STOP')24372438def test_queue():2439 q = multiprocessing.Queue()24402441 p = multiprocessing.Process(target=queue_func, args=(q,))2442 p.start()24432444 o = None2445 while o != 'STOP':2446 try:2447 o = q.get(timeout=0.3)2448 print(o, end=' ')2449 sys.stdout.flush()2450 except Empty:2451 print('TIMEOUT')24522453 print()24542455#### TEST_CONDITION24562457def condition_func(cond):2458 cond.acquire()2459 print('\t' + str(cond))2460 time.sleep(2)2461 print('\tchild is notifying')2462 print('\t' + str(cond))2463 cond.notify()2464 cond.release()24652466def test_condition():2467 cond = multiprocessing.Condition()24682469 p = multiprocessing.Process(target=condition_func, args=(cond,))2470 print(cond)24712472 cond.acquire()2473 print(cond)2474 cond.acquire()2475 print(cond)24762477 p.start()24782479 print('main is waiting')2480 cond.wait()2481 print('main has woken up')24822483 print(cond)2484 cond.release()2485 print(cond)2486 cond.release()24872488 p.join()2489 print(cond)24902491#### TEST_SEMAPHORE24922493def semaphore_func(sema, mutex, running):2494 sema.acquire()24952496 mutex.acquire()2497 running.value += 12498 print(running.value, 'tasks are running')2499 mutex.release()25002501 random.seed()2502 time.sleep(random.random()*2)25032504 mutex.acquire()2505 running.value -= 12506 print('%s has finished' % multiprocessing.current_process())2507 mutex.release()25082509 sema.release()25102511def test_semaphore():2512 sema = multiprocessing.Semaphore(3)2513 mutex = multiprocessing.RLock()2514 running = multiprocessing.Value('i', 0)25152516 processes = [2517 multiprocessing.Process(target=semaphore_func,2518 args=(sema, mutex, running))2519 for i in range(10)2520 ]25212522 for p in processes:2523 p.start()25242525 for p in processes:2526 p.join()25272528#### TEST_JOIN_TIMEOUT25292530def join_timeout_func():2531 print('\tchild sleeping')2532 time.sleep(5.5)2533 print('\n\tchild terminating')25342535def test_join_timeout():2536 p = multiprocessing.Process(target=join_timeout_func)2537 p.start()25382539 print('waiting for process to finish')25402541 while 1:2542 p.join(timeout=1)2543 if not p.is_alive():2544 break2545 print('.', end=' ')2546 sys.stdout.flush()25472548#### TEST_EVENT25492550def event_func(event):2551 print('\t%r is waiting' % multiprocessing.current_process())2552 event.wait()2553 print('\t%r has woken up' % multiprocessing.current_process())25542555def test_event():2556 event = multiprocessing.Event()25572558 processes = [multiprocessing.Process(target=event_func, args=(event,))2559 for i in range(5)]25602561 for p in processes:2562 p.start()25632564 print('main is sleeping')2565 time.sleep(2)25662567 print('main is setting event')2568 event.set()25692570 for p in processes:2571 p.join()25722573#### TEST_SHAREDVALUES25742575def sharedvalues_func(values, arrays, shared_values, shared_arrays):2576 for i in range(len(values)):2577 v = values[i][1]2578 sv = shared_values[i].value2579 assert v == sv25802581 for i in range(len(values)):2582 a = arrays[i][1]2583 sa = list(shared_arrays[i][:])2584 assert a == sa25852586 print('Tests passed')25872588def test_sharedvalues():2589 values = [2590 ('i', 10),2591 ('h', -2),2592 ('d', 1.25)2593 ]2594 arrays = [2595 ('i', list(range(100))),2596 ('d', [0.25 * i for i in range(100)]),2597 ('H', list(range(1000)))2598 ]25992600 shared_values = [multiprocessing.Value(id, v) for id, v in values]2601 shared_arrays = [multiprocessing.Array(id, a) for id, a in arrays]26022603 p = multiprocessing.Process(2604 target=sharedvalues_func,2605 args=(values, arrays, shared_values, shared_arrays)2606 )2607 p.start()2608 p.join()26092610 assert p.exitcode == 026112612####26132614def test(namespace=multiprocessing):2615 global multiprocessing26162617 multiprocessing = namespace26182619 for func in [ test_value, test_queue, test_condition,2620 test_semaphore, test_join_timeout, test_event,2621 test_sharedvalues ]:26222623 print('\n\t######## %s\n' % func.__name__)2624 func()26252626 ignore = multiprocessing.active_children() # cleanup any old processes2627 if hasattr(multiprocessing, '_debug_info'):2628 info = multiprocessing._debug_info()2629 if info:2630 print(info)2631 raise ValueError('there should be no positive refcounts left')26322633if __name__ == '__main__':2634 multiprocessing.freeze_support()26352636 assert len(sys.argv) in (1, 2)26372638 if len(sys.argv) == 1 or sys.argv[1] == 'processes':2639 print(' Using processes '.center(79, '-'))2640 namespace = multiprocessing2641 elif sys.argv[1] == 'manager':2642 print(' Using processes and a manager '.center(79, '-'))2643 namespace = multiprocessing.Manager()2644 namespace.Process = multiprocessing.Process2645 namespace.current_process = multiprocessing.current_process2646 namespace.active_children = multiprocessing.active_children2647 elif sys.argv[1] == 'threads':2648 print(' Using threads '.center(79, '-'))2649 import multiprocessing.dummy as namespace2650 else:2651 print('Usage:\n\t%s [processes | manager | threads]' % sys.argv[0])2652 raise SystemExit(2)26532654 test(namespace)2655```26562657Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:26582659```python2660#2661# Простой пример использования пула рабочих процессов для выполнения задач.2662#2663# Обратите внимание, что результаты, скорее всего, не будут получены из выходной2664# очереди в том же порядке, в котором соответствующие задачи были2665# помещены во входную очередь. Если важно получить результаты2666# в исходном порядке, рассмотрите использование `Pool.map()` или2667# `Pool.imap()` (что в любом случае сократит объём необходимого кода).2668#2669# Авторское право (c) 2006-2008, R Oudkerk2670# Все права защищены.2671#26722673import time2674import random26752676from multiprocessing import Process, Queue, current_process, freeze_support26772678#2679# Функция, выполняемая рабочими процессами2680#26812682def worker(input, output):2683 for func, args in iter(input.get, 'STOP'):2684 result = calculate(func, args)2685 output.put(result)26862687#2688# Функция, используемая для вычисления результата2689#26902691def calculate(func, args):2692 result = func(*args)2693 return '%s says that %s%s = %s' % \2694 (current_process().name, func.__name__, args, result)26952696#2697# Функции, на которые ссылаются задачи2698#26992700def mul(a, b):2701 time.sleep(0.5*random.random())2702 return a * b27032704def plus(a, b):2705 time.sleep(0.5*random.random())2706 return a + b27072708#2709#2710#27112712def test():2713 NUMBER_OF_PROCESSES = 42714 TASKS1 = [(mul, (i, 7)) for i in range(20)]2715 TASKS2 = [(plus, (i, 8)) for i in range(10)]27162717 # Создание очередей2718 task_queue = Queue()2719 done_queue = Queue()27202721 # Отправка задач2722 for task in TASKS1:2723 task_queue.put(task)27242725 # Запуск рабочих процессов2726 for i in range(NUMBER_OF_PROCESSES):2727 Process(target=worker, args=(task_queue, done_queue)).start()27282729 # Получение и вывод результатов2730 print('Unordered results:')2731 for i in range(len(TASKS1)):2732 print('\t', done_queue.get())27332734 # Добавить ещё задач с помощью `put()`2735 for task in TASKS2:2736 task_queue.put(task)27372738 # Получить и вывести ещё несколько результатов2739 for i in range(len(TASKS2)):2740 print('\t', done_queue.get())27412742 # Сообщить дочерним процессам об остановке2743 for i in range(NUMBER_OF_PROCESSES):2744 task_queue.put('STOP')27452746if __name__ == '__main__':2747 freeze_support()2748 test()2749```27502751Пример того, как пул рабочих процессов может запускать по одному экземпляру [`SimpleHTTPRequestHandler`](https://python-all.ru/3.1/library/http.server.html#http.server.SimpleHTTPRequestHandler), разделяя один слушающий сокет.27522753```python2754#2755# Пример, в котором пул HTTP-серверов использует один общий слушающий сокет2756#2757# В Windows этот модуль зависит от возможности сериализации сокета с помощью pickle2758# объекта, чтобы рабочие процессы могли унаследовать копию сервера2759# объект. (Мы импортируем `multiprocessing.reduction`, чтобы включить эту сериализацию.)2760#2761# Не уверены, нужно ли синхронизировать доступ к методу `socket.accept()` с помощью2762# блокировки, разделяемой между процессами – похоже, это не обязательно.2763#2764# Авторское право (c) 2006-2008, R Oudkerk2765# Все права защищены.2766#27672768import os2769import sys27702771from multiprocessing import Process, current_process, freeze_support2772from http.server import HTTPServer2773from http.server import SimpleHTTPRequestHandler27742775if sys.platform == 'win32':2776 import multiprocessing.reduction # сделать сокеты сериализуемыми/наследуемыми27772778def note(format, *args):2779 sys.stderr.write('[%s]\t%s\n' % (current_process().name, format%args))27802781class RequestHandler(SimpleHTTPRequestHandler):2782 # мы переопределяем log_message(), чтобы показывать, какой процесс обрабатывает запрос2783 def log_message(self, format, *args):2784 note(format, *args)27852786def serve_forever(server):2787 note('starting server')2788 try:2789 server.serve_forever()2790 except KeyboardInterrupt:2791 pass27922793def runpool(address, number_of_processes):2794 # создать единственный объект сервера – каждый дочерний процесс унаследует его копию2795 server = HTTPServer(address, RequestHandler)27962797 # создать дочерние процессы, которые будут работать как воркеры2798 for i in range(number_of_processes-1):2799 Process(target=serve_forever, args=(server,)).start()28002801 # главный процесс также выступает в роли воркера2802 serve_forever(server)28032804def test():2805 DIR = os.path.join(os.path.dirname(__file__), '..')2806 ADDRESS = ('localhost', 8000)2807 NUMBER_OF_PROCESSES = 428082809 print('Serving at http://%s:%d using %d worker processes' % \2810 (ADDRESS[0], ADDRESS[1], NUMBER_OF_PROCESSES))2811 print('To exit press Ctrl-' + ['C', 'Break'][sys.platform=='win32'])28122813 os.chdir(DIR)2814 runpool(ADDRESS, NUMBER_OF_PROCESSES)28152816if __name__ == '__main__':2817 freeze_support()2818 test()2819```28202821Некоторые простые тесты производительности, сравнивающие `multiprocessing` с [`threading`](https://python-all.ru/3.1/library/threading.html#module-threading):28222823```python2824#2825# Простые тесты производительности для пакета multiprocessing2826#2827# Авторское право (c) 2006-2008, R Oudkerk2828# Все права защищены.2829#28302831import time, sys, multiprocessing, threading, queue, gc28322833if sys.platform == 'win32':2834 _timer = time.clock2835else:2836 _timer = time.time28372838delta = 128392840#### TEST_QUEUESPEED28412842def queuespeed_func(q, c, iterations):2843 a = '0' * 2562844 c.acquire()2845 c.notify()2846 c.release()28472848 for i in range(iterations):2849 q.put(a)28502851 q.put('STOP')28522853def test_queuespeed(Process, q, c):2854 elapsed = 02855 iterations = 128562857 while elapsed < delta:2858 iterations *= 228592860 p = Process(target=queuespeed_func, args=(q, c, iterations))2861 c.acquire()2862 p.start()2863 c.wait()2864 c.release()28652866 result = None2867 t = _timer()28682869 while result != 'STOP':2870 result = q.get()28712872 elapsed = _timer() - t28732874 p.join()28752876 print(iterations, 'objects passed through the queue in', elapsed, 'seconds')2877 print('average number/sec:', iterations/elapsed)28782879#### TEST_PIPESPEED28802881def pipe_func(c, cond, iterations):2882 a = '0' * 2562883 cond.acquire()2884 cond.notify()2885 cond.release()28862887 for i in range(iterations):2888 c.send(a)28892890 c.send('STOP')28912892def test_pipespeed():2893 c, d = multiprocessing.Pipe()2894 cond = multiprocessing.Condition()2895 elapsed = 02896 iterations = 128972898 while elapsed < delta:2899 iterations *= 229002901 p = multiprocessing.Process(target=pipe_func,2902 args=(d, cond, iterations))2903 cond.acquire()2904 p.start()2905 cond.wait()2906 cond.release()29072908 result = None2909 t = _timer()29102911 while result != 'STOP':2912 result = c.recv()29132914 elapsed = _timer() - t2915 p.join()29162917 print(iterations, 'objects passed through connection in',elapsed,'seconds')2918 print('average number/sec:', iterations/elapsed)29192920#### TEST_SEQSPEED29212922def test_seqspeed(seq):2923 elapsed = 02924 iterations = 129252926 while elapsed < delta:2927 iterations *= 229282929 t = _timer()29302931 for i in range(iterations):2932 a = seq[5]29332934 elapsed = _timer()-t29352936 print(iterations, 'iterations in', elapsed, 'seconds')2937 print('average number/sec:', iterations/elapsed)29382939#### TEST_LOCK29402941def test_lockspeed(l):2942 elapsed = 02943 iterations = 129442945 while elapsed < delta:2946 iterations *= 229472948 t = _timer()29492950 for i in range(iterations):2951 l.acquire()2952 l.release()29532954 elapsed = _timer()-t29552956 print(iterations, 'iterations in', elapsed, 'seconds')2957 print('average number/sec:', iterations/elapsed)29582959#### TEST_CONDITION29602961def conditionspeed_func(c, N):2962 c.acquire()2963 c.notify()29642965 for i in range(N):2966 c.wait()2967 c.notify()29682969 c.release()29702971def test_conditionspeed(Process, c):2972 elapsed = 02973 iterations = 129742975 while elapsed < delta:2976 iterations *= 229772978 c.acquire()2979 p = Process(target=conditionspeed_func, args=(c, iterations))2980 p.start()29812982 c.wait()29832984 t = _timer()29852986 for i in range(iterations):2987 c.notify()2988 c.wait()29892990 elapsed = _timer()-t29912992 c.release()2993 p.join()29942995 print(iterations * 2, 'waits in', elapsed, 'seconds')2996 print('average number/sec:', iterations * 2 / elapsed)29972998####29993000def test():3001 manager = multiprocessing.Manager()30023003 gc.disable()30043005 print('\n\t######## testing Queue.Queue\n')3006 test_queuespeed(threading.Thread, queue.Queue(),3007 threading.Condition())3008 print('\n\t######## testing multiprocessing.Queue\n')3009 test_queuespeed(multiprocessing.Process, multiprocessing.Queue(),3010 multiprocessing.Condition())3011 print('\n\t######## testing Queue managed by server process\n')3012 test_queuespeed(multiprocessing.Process, manager.Queue(),3013 manager.Condition())3014 print('\n\t######## testing multiprocessing.Pipe\n')3015 test_pipespeed()30163017 print()30183019 print('\n\t######## testing list\n')3020 test_seqspeed(list(range(10)))3021 print('\n\t######## testing list managed by server process\n')3022 test_seqspeed(manager.list(list(range(10))))3023 print('\n\t######## testing Array("i", ..., lock=False)\n')3024 test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=False))3025 print('\n\t######## testing Array("i", ..., lock=True)\n')3026 test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=True))30273028 print()30293030 print('\n\t######## testing threading.Lock\n')3031 test_lockspeed(threading.Lock())3032 print('\n\t######## testing threading.RLock\n')3033 test_lockspeed(threading.RLock())3034 print('\n\t######## testing multiprocessing.Lock\n')3035 test_lockspeed(multiprocessing.Lock())3036 print('\n\t######## testing multiprocessing.RLock\n')3037 test_lockspeed(multiprocessing.RLock())3038 print('\n\t######## testing lock managed by server process\n')3039 test_lockspeed(manager.Lock())3040 print('\n\t######## testing rlock managed by server process\n')3041 test_lockspeed(manager.RLock())30423043 print()30443045 print('\n\t######## testing threading.Condition\n')3046 test_conditionspeed(threading.Thread, threading.Condition())3047 print('\n\t######## testing multiprocessing.Condition\n')3048 test_conditionspeed(multiprocessing.Process, multiprocessing.Condition())3049 print('\n\t######## testing condition managed by a server process\n')3050 test_conditionspeed(multiprocessing.Process, manager.Condition())30513052 gc.enable()30533054if __name__ == '__main__':3055 multiprocessing.freeze_support()3056 test()3057```3058