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

multiprocessing.md

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

1> **Источник:** https://python-all.ru/3.2/library/multiprocessing.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 16.3. [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) – Параллелизм на основе процессов89## 16.3.1. Введение1011Пакет [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) поддерживает порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/3.2/library/threading.html#module-threading). Пакет [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) предоставляет как локальный, так и удалённый параллелизм, обходя [*Глобальную блокировку интерпретатора*](https://python-all.ru/3.2/glossary.html#term-global-interpreter-lock) с помощью подпроцессов вместо потоков. Благодаря этому модуль [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) позволяет программисту полностью задействовать несколько процессоров на данной машине. Он работает как на Unix, так и на Windows.1213> **Примечание**14>15> Часть функционала этого пакета требует работающей реализации разделяемого семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки его импорта приведут к [`ImportError`](https://python-all.ru/3.2/library/exceptions.html#ImportError). Дополнительную информацию см. в [issue 3770](https://python-all.ru/3.2/library/multiprocessing.html).1617> **Примечание**18>19> Функциональность этого пакета требует, чтобы модуль `__main__` был импортируем дочерними процессами. Это описано в [*правилах программирования*](https://python-all.ru/3.2/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.2/library/multiprocessing.html#multiprocessing.Process)4041В [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) процессы порождаются созданием объекта [`Process`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.start). [`Process`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process) следует API [`threading.Thread`](https://python-all.ru/3.2/library/threading.html#threading.Thread). Пример простой многопроцессной программы:4243```python44from multiprocessing import Process4546def f(name):47    print('hello', name)4849if __name__ == '__main__':50    p = Process(target=f, args=('bob',))51    p.start()52    p.join()53```5455Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:5657```python58from multiprocessing import Process59import os6061def info(title):62    print(title)63    print('module name:', __name__)64    if hasattr(os, 'getppid'):  # доступно только на Unix65        print('parent process:', os.getppid())66    print('process id:', os.getpid())6768def f(name):69    info('function f')70    print('hello', name)7172if __name__ == '__main__':73    info('main line')74    p = Process(target=f, args=('bob',))75    p.start()76    p.join()77```7879Зачем (в Windows) нужна конструкция `if __name__ == '__main__'`, объясняется в разделе [*Рекомендации по программированию*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-programming).8081### 16.3.1.2. Обмен объектами между процессами8283[`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) поддерживает два типа каналов связи между процессами:8485**Очереди**8687> Класс [`Queue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue) является почти копией [`queue.Queue`](https://python-all.ru/3.2/library/queue.html#queue.Queue). Например:88>89> ```python90> from multiprocessing import Process, Queue91>92> def f(q):93>     q.put([42, None, 'hello'])94>95> if __name__ == '__main__':96>     q = Queue()97>     p = Process(target=f, args=(q,))98>     p.start()99>     print(q.get())    # печатает "[42, None, 'hello']"100>     p.join()101> ```102>103> Очереди безопасны для потоков и процессов, но учтите, что они никогда не должны создаваться как побочный эффект импорта модуля: это может привести к взаимоблокировке! (см. [*Импорт в многопоточном коде*](https://python-all.ru/3.2/library/threading.html#threaded-imports))104105**Каналы**106107> Функция [`Pipe()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является двунаправленным. Например:108>109> ```python110> from multiprocessing import Process, Pipe111>112> def f(conn):113>     conn.send([42, None, 'hello'])114>     conn.close()115>116> if __name__ == '__main__':117>     parent_conn, child_conn = Pipe()118>     p = Process(target=f, args=(child_conn,))119>     p.start()120>     print(parent_conn.recv())   # печатает "[42, None, 'hello']"121>     p.join()122> ```123>124> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы [`send()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection.send) и [`recv()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection.recv) (среди прочих). Следует учитывать, что данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.125126### 16.3.1.3. Синхронизация между процессами127128[`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) содержит эквиваленты всех примитивов синхронизации из [`threading`](https://python-all.ru/3.2/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс одновременно выводит на стандартный вывод:129130```python131from multiprocessing import Process, Lock132133def f(l, i):134    l.acquire()135    print('hello world', i)136    l.release()137138if __name__ == '__main__':139    lock = Lock()140141    for num in range(10):142        Process(target=f, args=(lock, num)).start()143```144145Без использования блокировки вывод от разных процессов может полностью перепутаться.146147### 16.3.1.4. Обмен состоянием между процессами148149Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.150151Однако, если действительно необходимо использовать общие данные, [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) предоставляет несколько способов это сделать.152153**Разделяемая память**154155> Данные могут храниться в карте разделяемой памяти с помощью [`Value`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Array). Например, следующий код156>157> ```python158> from multiprocessing import Process, Value, Array159>160> def f(n, a):161>     n.value = 3.1415927162>     for i in range(len(a)):163>         a[i] = -a[i]164>165> if __name__ == '__main__':166>     num = Value('d', 0.0)167>     arr = Array('i', range(10))168>169>     p = Process(target=f, args=(num, arr))170>     p.start()171>     p.join()172>173>     print(num.value)174>     print(arr[:])175> ```176>177> выведет178>179> ```python180> 3.1415927181> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]182> ```183>184> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, как в модуле [`array`](https://python-all.ru/3.2/library/array.html#module-array): `'d'` обозначает число двойной точности с плавающей точкой, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасны для работы с процессами и потоками.185>186> Для большей гибкости при использовании разделяемой памяти можно воспользоваться модулем [`multiprocessing.sharedctypes`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing.sharedctypes), который поддерживает создание произвольных объектов ctypes, размещённых в разделяемой памяти.187188**Серверный процесс**189190> Объект менеджера, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими через прокси.191>192> Менеджер, возвращаемый `Manager()`, будет поддерживать типы [`list`](https://python-all.ru/3.2/library/functions.html#list), [`dict`](https://python-all.ru/3.2/library/stdtypes.html#dict), `Namespace`, [`Lock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Event), [`Queue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Array). Например,193>194> ```python195> from multiprocessing import Process, Manager196>197> def f(d, l):198>     d[1] = '1'199>     d['2'] = 2200>     d[0.25] = None201>     l.reverse()202>203> if __name__ == '__main__':204>     manager = Manager()205>206>     d = manager.dict()207>     l = manager.list(range(10))208>209>     p = Process(target=f, args=(d, l))210>     p.start()211>     p.join()212>213>     print(d)214>     print(l)215> ```216>217> выведет218>219> ```python220> {0.25: None, 1: '1', '2': 2}221> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]222> ```223>224> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.225226### 16.3.1.5. Использование пула рабочих процессов227228Класс `Pool` представляет пул рабочих процессов. Он имеет методы, которые позволяют делегировать задачи рабочим процессам несколькими различными способами.229230Например:231232```python233from multiprocessing import Pool234235def f(x):236    return x*x237238if __name__ == '__main__':239    pool = Pool(processes=4)               # запустить 4 рабочих процесса240    result = pool.apply_async(f, [10])     # асинхронно вычислить "f(10)"241    print(result.get(timeout=1))           # выводит "100", если компьютер не *очень* медленный242    print(pool.map(f, range(10)))          # выводит "[0, 1, 4,..., 81]"243```244245## 16.3.2. Справочник246247Пакет [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) в основном повторяет API модуля [`threading`](https://python-all.ru/3.2/library/threading.html#module-threading).248249### 16.3.2.1. [`Process`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process) и исключения250251#### `class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})`252253Объекты Process представляют активность, которая выполняется в отдельном процессе. Класс [`Process`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process) содержит эквиваленты всех методов [`threading.Thread`](https://python-all.ru/3.2/library/threading.html#threading.Thread).254255Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должно быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/3.2/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию он равен `None`, то есть ничего не вызывается. *name* – это имя процесса. По умолчанию генерируется уникальное имя вида 'Process-N1:N2:...:Nk', где N1,N2,...,Nk – последовательность целых чисел, длина которой определяется *поколением* процесса. *args* – кортеж аргументов для вызова цели. *kwargs* – словарь именованных аргументов для вызова цели. По умолчанию в *target* не передается никаких аргументов.256257Если подкласс переопределяет конструктор, он должен убедиться, что вызывает конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.258259#### `run()`260261Метод, представляющий активность процесса.262263Вы можете переопределить этот метод в подклассе. Стандартный метод [`run()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.264265#### `start()`266267Запускает активность процесса.268269Этот метод должен вызываться не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.270271#### `join([timeout])`272273Блокирует вызывающий поток до тех пор, пока процесс, для которого вызван метод [`join()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.join), не завершится или не истечет указанное время ожидания.274275Если *timeout* равен `None`, то время ожидания не ограничено.276277Процесс может быть присоединён много раз.278279Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.280281#### `name`282283Имя процесса.284285Имя – это строка, используемая только для идентификации. Она не несет семантической нагрузки. Несколько процессов могут иметь одинаковое имя. Начальное имя задается конструктором.286287#### `is_alive()`288289Возвращает, жив ли процесс.290291Грубо говоря, объект процесса считается живым с момента возврата метода [`start()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.292293#### `daemon`294295Флаг daemon процесса, логическое значение. Должен быть установлен до вызова [`start()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.start).296297Начальное значение наследуется от создающего процесса.298299Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.300301Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.302303В дополнение к API `Threading.Thread`, объекты [`Process`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process) также поддерживают следующие атрибуты и методы:304305#### `pid`306307Возвращает идентификатор процесса. До запуска процесса будет `None`.308309#### `exitcode`310311Код завершения дочернего процесса. Будет `None`, если процесс ещё не завершился. Отрицательное значение *-N* указывает, что дочерний процесс был завершён сигналом *N*.312313#### `authkey`314315Ключ аутентификации процесса (байтовая строка).316317При инициализации [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) главному процессу присваивается случайная строка, сгенерированная с помощью `os.random()`.318319При создании объекта [`Process`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process) он наследует ключ аутентификации своего родительского процесса, хотя это можно изменить, установив [`authkey`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.authkey) в другую строку байтов.320321См. [*Ключи аутентификации*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-auth-keys).322323#### `terminate()`324325Завершает процесс. В Unix для этого используется сигнал `SIGTERM`; в Windows – `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.п. выполнены не будут.326327Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.328329> **Предупреждение**330>331> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.332333Следует отметить, что [`start()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.is_alive), [`terminate()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.terminate) и `exit_code` методы должны вызываться только тем процессом, который создал объект процесса.334335Пример использования некоторых методов [`Process`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process):336337```python338>>> import multiprocessing, time, signal339>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))340>>> print(p, p.is_alive())341<Process(Process-1, initial)> False342>>> p.start()343>>> print(p, p.is_alive())344<Process(Process-1, started)> True345>>> p.terminate()346>>> time.sleep(0.1)347>>> print(p, p.is_alive())348<Process(Process-1, stopped[SIGTERM])> False349>>> p.exitcode == -signal.SIGTERM350True351```352353#### `exception multiprocessing.BufferTooShort`354355Исключение, вызываемое [`Connection.recv_bytes_into()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection.recv_bytes_into), когда переданный объект буфера слишком мал для читаемого сообщения.356357Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде строки байтов.358359### 16.3.2.2. Каналы и очереди360361При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.362363Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает множество производителей и потребителей).364365Типы [`Queue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue), [`multiprocessing.queues.SimpleQueue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.multiprocessing.queues.SimpleQueue) и [`JoinableQueue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue) представляют собой очереди FIFO с множеством производителей и потребителей, построенные по образцу класса [`queue.Queue`](https://python-all.ru/3.2/library/queue.html#queue.Queue) из стандартной библиотеки. Отличие заключается в том, что [`Queue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue) не имеет методов [`task_done()`](https://python-all.ru/3.2/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.2/library/queue.html#queue.Queue.join), которые были введены в класс [`queue.Queue`](https://python-all.ru/3.2/library/queue.html#queue.Queue) в Python 2.5.366367Если используется [`JoinableQueue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue), то **необходимо** вызывать [`JoinableQueue.task_done()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди, иначе семафор, используемый для подсчёта количества незавершённых задач, может в конечном счёте переполниться, что приведёт к возникновению исключения.368369Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [*Менеджеры*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-managers).370371> **Примечание**372>373> [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) использует стандартные исключения [`queue.Empty`](https://python-all.ru/3.2/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.2/library/queue.html#queue.Full) для сигнализации о тайм-ауте. Они недоступны в пространстве имён [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing), поэтому их необходимо импортировать из [`queue`](https://python-all.ru/3.2/library/queue.html#module-queue).374375> **Предупреждение**376>377> Если процесс завершается с помощью [`Process.terminate()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/3.2/library/os.html#os.kill) в то время, когда он пытается использовать [`Queue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue), данные в очереди скорее всего будут повреждены. Из-за этого любой другой процесс может получить исключение при попытке использовать очередь впоследствии.378379> **Предупреждение**380>381> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не вызывал `JoinableQueue.cancel_join_thread()`), то этот процесс не завершится, пока все буферизованные элементы не будут сброшены в канал.382>383> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.384>385> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [*Рекомендации по программированию*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-programming).386387Пример использования очередей для межпроцессного взаимодействия см. в разделе [*Примеры*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-examples).388389#### `multiprocessing.Pipe([duplex])`390391Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection), представляющих концы канала.392393Если *duplex* равен `True` (по умолчанию), то канал двунаправленный. Если *duplex* равен `False`, то канал однонаправленный: `conn1` может использоваться только для получения сообщений, а `conn2` – только для отправки сообщений.394395#### `class multiprocessing.Queue([maxsize])`396397Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.398399Обычные исключения [`queue.Empty`](https://python-all.ru/3.2/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.2/library/queue.html#queue.Full) из модуля `Queue` стандартной библиотеки возбуждаются для указания тайм-аутов.400401[`Queue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue) реализует все методы [`queue.Queue`](https://python-all.ru/3.2/library/queue.html#queue.Queue), за исключением [`task_done()`](https://python-all.ru/3.2/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.2/library/queue.html#queue.Queue.join).402403#### `qsize()`404405Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.406407Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/3.2/library/exceptions.html#NotImplementedError) на платформах Unix, таких как Mac OS X, где `sem_getvalue()` не реализован.408409#### `empty()`410411Возвращает `True`, если очередь пуста, и `False` в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.412413#### `full()`414415Возвращает `True`, если очередь полна, и `False` в противном случае. Из-за семантики многопоточности/многопроцессорности это ненадёжно.416417#### `put(obj[, block[, timeout]])`418419Помещает obj в очередь. Если необязательный аргумент *block* равен `True` (по умолчанию), а *timeout* равен `None` (по умолчанию), то блокируется при необходимости до появления свободного места. Если *timeout* – положительное число, то блокируется не более *timeout* секунд и вызывает исключение [`queue.Full`](https://python-all.ru/3.2/library/queue.html#queue.Full), если в течение этого времени свободное место не появилось. В противном случае (*block* равен `False`), помещает элемент в очередь, если свободное место доступно немедленно, иначе вызывает исключение [`queue.Full`](https://python-all.ru/3.2/library/queue.html#queue.Full) (*timeout* в этом случае игнорируется).420421#### `put_nowait(obj)`422423Эквивалентно `put(obj, False)`.424425#### `get([block[, timeout]])`426427Remove and return an item from the queue. If optional args *block* is `True` (the default) and *timeout* is `None` (the default), block if necessary until an item is available. If *timeout* is a positive number, it blocks at most *timeout* seconds and raises the [`queue.Empty`](https://python-all.ru/3.2/library/queue.html#queue.Empty) exception if no item was available within that time. Otherwise (block is `False`), return an item if one is immediately available, else raise the [`queue.Empty`](https://python-all.ru/3.2/library/queue.html#queue.Empty) exception (*timeout* is ignored in that case).428429#### `get_nowait()`430431Эквивалентно `get(False)`.432433У [`multiprocessing.Queue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue) есть несколько дополнительных методов, отсутствующих в [`queue.Queue`](https://python-all.ru/3.2/library/queue.html#queue.Queue). В большинстве кода эти методы обычно не нужны:434435#### `close()`436437Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.438439#### `join_thread()`440441Присоединяет фоновый поток. Используется только после вызова [`close()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue.close). Блокирует выполнение до завершения фонового потока, гарантируя, что все данные из буфера будут сброшены в канал.442443По умолчанию, если процесс не является создателем очереди, то при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы [`join_thread()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue.join_thread) не делала ничего.444445#### `cancel_join_thread()`446447Предотвращает блокирование [`join_thread()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue.join_thread). В частности, это не даёт фоновому потоку автоматически присоединиться при завершении процесса – см. [`join_thread()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue.join_thread).448449#### `class multiprocessing.queues.SimpleQueue`450451Упрощённый тип [`очереди`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue), очень близкий к блокируемому [`Pipe`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Pipe).452453#### `empty()`454455Возвращает `True`, если очередь пуста, и `False` в противном случае.456457#### `get()`458459Извлекает и возвращает элемент из очереди.460461#### `put(item)`462463Помещает *item* в очередь.464465#### `class multiprocessing.JoinableQueue([maxsize])`466467[`JoinableQueue`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue), подкласс [`очереди`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно содержит методы [`task_done()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue.join).468469#### `task_done()`470471Указывает, что ранее поставленная в очередь задача выполнена. Используется потоками-потребителями очереди. Для каждого вызова [`get()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue.get), используемого для получения задачи, последующий вызов [`task_done()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) сообщает очереди, что обработка задачи завершена.472473Если `join()` в данный момент блокируется, он возобновит работу, когда все элементы будут обработаны (т.е. когда для каждого элемента, помещенного в очередь с помощью [`put()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Queue.put), будет получен вызов [`task_done()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done)).474475Вызывает [`ValueError`](https://python-all.ru/3.2/library/exceptions.html#ValueError), если вызывается больше раз, чем было помещено элементов в очередь.476477#### `join()`478479Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.480481Счетчик незавершенных задач увеличивается при каждом добавлении элемента в очередь. Он уменьшается, когда поток-потребитель вызывает [`task_done()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы указать, что элемент получен и вся работа с ним завершена. Когда счетчик незавершенных задач падает до нуля, `join()` разблокируется.482483### 16.3.2.3. Разное484485#### `multiprocessing.active_children()`486487Возвращает список всех активных дочерних процессов текущего процесса.488489Вызов этой функции приводит к «присоединению» любых процессов, которые уже завершились.490491#### `multiprocessing.cpu_count()`492493Возвращает количество процессоров в системе. Может вызвать [`NotImplementedError`](https://python-all.ru/3.2/library/exceptions.html#NotImplementedError).494495#### `multiprocessing.current_process()`496497Возвращает объект [`процесса`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.498499Аналог [`threading.current_thread()`](https://python-all.ru/3.2/library/threading.html#threading.current_thread).500501#### `multiprocessing.freeze_support()`502503Добавлена поддержка для случая, когда программа, использующая [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing), была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)504505Эту функцию нужно вызывать сразу после строки `if __name__ == '__main__'` главного модуля. Например:506507```python508from multiprocessing import Process, freeze_support509510def f():511    print('hello world!')512513if __name__ == '__main__':514    freeze_support()515    Process(target=f).start()516```517518Если строку `freeze_support()` опустить, то при попытке запустить замороженный исполняемый файл будет вызвано [`RuntimeError`](https://python-all.ru/3.2/library/exceptions.html#RuntimeError).519520Если модуль запускается обычным образом интерпретатором Python, то [`freeze_support()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.freeze_support) не действует.521522#### `multiprocessing.set_executable()`523524Задаёт путь к интерпретатору Python, который будет использоваться при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/3.2/library/sys.html#sys.executable)). Встраивающим Python разработчикам, вероятно, потребуется сделать что-то вроде525526```python527set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))528```529530прежде чем они смогут создавать дочерние процессы. (Только для Windows)531532> **Примечание**533>534> [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) не содержит аналогов [`threading.active_count()`](https://python-all.ru/3.2/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/3.2/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/3.2/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/3.2/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/3.2/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/3.2/library/threading.html#threading.local).535536### 16.3.2.4. Объекты соединений537538Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.539540Объекты соединения обычно создаются с помощью [`Pipe()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Pipe) – см. также [*Слушатели и клиенты*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-listeners-clients).541542#### `class multiprocessing.Connection`543544#### `send(obj)`545546Отправляет объект на другой конец соединения; этот объект должен быть прочитан с помощью [`recv()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection.recv).547548Объект должен быть сериализуем (picklable). Очень большие объекты (примерно от 32 МБ, хотя это зависит от ОС) могут вызвать исключение ValueError.549550#### `recv()`551552Возвращает объект, отправленный с другого конца соединения с помощью [`send()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection.send). Блокируется, пока не появится что-то для приёма. Возбуждает [`EOFError`](https://python-all.ru/3.2/library/exceptions.html#EOFError), если больше нечего принимать и другой конец был закрыт.553554#### `fileno()`555556Возвращает файловый дескриптор или дескриптор (handle), используемый подключением.557558#### `close()`559560Закрывает подключение.561562Вызывается автоматически при сборке мусора для подключения.563564#### `poll([timeout])`565566Возвращает, есть ли данные для чтения.567568Если *timeout* не указан, то возврат происходит немедленно. Если *timeout* – число, оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, используется бесконечное ожидание.569570#### `send_bytes(buffer[, offset[, size]])`571572Отправляет байтовые данные из объекта, поддерживающего интерфейс буфера, в виде целого сообщения.573574Если задан *offset*, то данные читаются с этой позиции в *buffer*. Если задан *size*, то из буфера будет прочитано соответствующее количество байт. Очень большие буферы (примерно от 32 МБ, хотя зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/3.2/library/exceptions.html#ValueError).575576#### `recv_bytes([maxlength])`577578Возвращает в виде строки полное байтовое сообщение, отправленное с другого конца соединения. Блокируется до появления данных для приёма. Вызывает [`EOFError`](https://python-all.ru/3.2/library/exceptions.html#EOFError), если больше нечего получать и другой конец закрыт.579580Если задан *maxlength* и сообщение длиннее *maxlength*, то генерируется [`IOError`](https://python-all.ru/3.2/library/exceptions.html#IOError), и соединение становится непригодным для чтения.581582#### `recv_bytes_into(buffer[, offset])`583584Читает в *buffer* полное байтовое сообщение, отправленное с другого конца соединения, и возвращает количество байт в сообщении. Блокируется до появления данных для приёма. Вызывает [`EOFError`](https://python-all.ru/3.2/library/exceptions.html#EOFError), если больше нечего получать и другой конец закрыт.585586*buffer* должен быть объектом, удовлетворяющим интерфейсу буфера для записи. Если задан *offset*, сообщение будет записано в буфер, начиная с этой позиции. Значение offset должно быть неотрицательным целым числом, меньшим длины *buffer* (в байтах).587588Если буфер слишком мал, то вызывается исключение [`BufferTooShort`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.BufferTooShort), и полное сообщение доступно как `e.args[0]`, где `e` – это экземпляр исключения.589590Например:591592```python593>>> from multiprocessing import Pipe594>>> a, b = Pipe()595>>> a.send([1, 'hello', None])596>>> b.recv()597[1, 'hello', None]598>>> b.send_bytes(b'thank you')599>>> a.recv_bytes()600b'thank you'601>>> import array602>>> arr1 = array.array('i', range(5))603>>> arr2 = array.array('i', [0] * 10)604>>> a.send_bytes(arr1)605>>> count = b.recv_bytes_into(arr2)606>>> assert count == len(arr1) * arr1.itemsize607>>> arr2608array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])609```610611> **Предупреждение**612>613> Метод [`Connection.recv()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection.recv) автоматически десериализует полученные данные, что может представлять угрозу безопасности, если нет доверия к процессу, отправившему сообщение.614>615> Поэтому, если объект соединения не был создан с помощью [`Pipe()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Pipe), методы [`recv()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection.recv) и [`send()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection.send) следует использовать только после выполнения некоторой аутентификации. См. [*Authentication keys*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-auth-keys).616617> **Предупреждение**618>619> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.620621### 16.3.2.5. Примитивы синхронизации622623Обычно примитивы синхронизации не так необходимы в многопроцессной программе, как в многопоточной. См. документацию модуля [`threading`](https://python-all.ru/3.2/library/threading.html#module-threading).624625Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [*Менеджеры*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-managers).626627#### `class multiprocessing.BoundedSemaphore([value])`628629Объект ограниченного семафора: копия [`threading.BoundedSemaphore`](https://python-all.ru/3.2/library/threading.html#threading.BoundedSemaphore).630631(На Mac OS X этот объект неотличим от [`Semaphore`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализована на этой платформе).632633#### `class multiprocessing.Condition([lock])`634635Условная переменная: клон [`threading.Condition`](https://python-all.ru/3.2/library/threading.html#threading.Condition).636637Если указана *блокировка*, то это должен быть объект [`Lock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.RLock) из [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing).638639#### `class multiprocessing.Event`640641Клон [`threading.Event`](https://python-all.ru/3.2/library/threading.html#threading.Event). Этот метод возвращает состояние внутреннего семафора при выходе, поэтому он всегда возвращает `True`, если только не задан тайм-аут и операция не истекает по времени.642643Изменено в версии 3.1: Ранее метод всегда возвращал `None`.644645#### `class multiprocessing.Lock`646647Объект нерекурсивной блокировки: копия [`threading.Lock`](https://python-all.ru/3.2/library/threading.html#threading.Lock).648649#### `class multiprocessing.RLock`650651Объект рекурсивной блокировки: копия [`threading.RLock`](https://python-all.ru/3.2/library/threading.html#threading.RLock).652653#### `class multiprocessing.Semaphore([value])`654655Объект семафора: копия [`threading.Semaphore`](https://python-all.ru/3.2/library/threading.html#threading.Semaphore).656657> **Примечание**658>659> В Mac OS X функция `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с таймаутом будет эмулировать её поведение с помощью цикла ожидания.660661> **Примечание**662>663> Если сигнал SIGINT, сгенерированный Ctrl-C, поступает в то время, как главный поток заблокирован вызовом `BoundedSemaphore.acquire()`, `Lock.acquire()`, `RLock.acquire()`, `Semaphore.acquire()`, `Condition.acquire()` или `Condition.wait()`, то вызов будет немедленно прерван и будет возбуждено [`KeyboardInterrupt`](https://python-all.ru/3.2/library/exceptions.html#KeyboardInterrupt).664>665> Это отличается от поведения [`threading`](https://python-all.ru/3.2/library/threading.html#module-threading), где SIGINT будет игнорироваться, пока выполняются эквивалентные блокирующие вызовы.666667### 16.3.2.6. Общие объекты [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes)668669Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.670671#### `multiprocessing.Value(typecode_or_type, *args[, lock])`672673Возвращает объект [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes), выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для этого объекта.674675*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.2/library/array.html#module-array). *\*args* передаётся конструктору типа.676677Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».678679Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.680681#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`682683Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.684685*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.2/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* является последовательностью, которая используется для инициализации массива, и её длина определяет длину массива.686687Если *блокировка* равно `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равно `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».688689Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.690691Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.692693#### 16.3.2.6.1. Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing.sharedctypes)694695Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing.sharedctypes) предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.696697> **Примечание**698>699> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.700701#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`702703Возвращает массив ctypes, выделенный из разделяемой памяти.704705*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.2/library/array.html#module-array). Если *size\_or\_initializer* является целым числом, то оно задаёт длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.706707Обратите внимание, что запись и чтение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.708709#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`710711Возвращает объект ctypes, выделенный из разделяемой памяти.712713*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, как в модуле [`array`](https://python-all.ru/3.2/library/array.html#module-array). *\*args* передаётся конструктору типа.714715Обратите внимание, что запись и чтение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.716717Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.2/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют хранить и извлекать строки – см. документацию по [`ctypes`](https://python-all.ru/3.2/library/ctypes.html#module-ctypes).718719#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *args[, lock])`720721То же, что и [`RawArray()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного массива ctypes.722723Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом `Lock` или `RLock`, то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».724725Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.726727#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args[, lock])`728729То же, что и [`RawValue()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), но в зависимости от значения *блокировка* может быть возвращена процесс-безопасная синхронизирующая обёртка вместо необработанного объекта ctypes.730731Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом `Lock` или `RLock`, то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».732733Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.734735#### `multiprocessing.sharedctypes.copy(obj)`736737Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes *obj*.738739#### `multiprocessing.sharedctypes.synchronized(obj[, lock])`740741Возвращает процесс-безопасный объект-обёртку для объекта ctypes, который использует *блокировка* для синхронизации доступа. Если *блокировка* равно `None` (по умолчанию), то автоматически создаётся объект [`multiprocessing.RLock`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.RLock).742743Синхронизированная обёртка будет иметь два дополнительных метода по сравнению с объектом, который она оборачивает: `get_obj()` возвращает обёрнутый объект, а `get_lock()` возвращает объект блокировки, используемый для синхронизации.744745Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.746747В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некий подкласс [`ctypes.Structure`](https://python-all.ru/3.2/library/ctypes.html#ctypes.Structure).)748749| ctypes | sharedctypes с типом | sharedctypes с typecode |750| --- | --- | --- |751| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |752| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) |  |753| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |754| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |755756Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:757758```python759from multiprocessing import Process, Lock760from multiprocessing.sharedctypes import Value, Array761from ctypes import Structure, c_double762763class Point(Structure):764    _fields_ = [('x', c_double), ('y', c_double)]765766def modify(n, x, s, A):767    n.value **= 2768    x.value **= 2769    s.value = s.value.upper()770    for a in A:771        a.x **= 2772        a.y **= 2773774if __name__ == '__main__':775    lock = Lock()776777    n = Value('i', 7)778    x = Value(c_double, 1.0/3.0, lock=False)779    s = Array('c', b'hello world', lock=lock)780    A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)781782    p = Process(target=modify, args=(n, x, s, A))783    p.start()784    p.join()785786    print(n.value)787    print(x.value)788    print(s.value)789    print([(a.x, a.y) for a in A])790```791792Выводятся следующие результаты:793794```text795497960.1111111111111111797HELLO WORLD798[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]799```800801### 16.3.2.7. Менеджеры802803Менеджеры позволяют создавать данные, которыми могут обмениваться разные процессы. Объект менеджера управляет серверным процессом, который ведает *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.804805#### `multiprocessing.Manager()`806807Возвращает запущенный объект [`SyncManager`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.SyncManager), который можно использовать для обмена объектами между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.808809Процессы менеджеров завершаются, как только они собираются сборщиком мусора или их родительский процесс завершается. Классы менеджеров определены в модуле [`multiprocessing.managers`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing.managers):810811#### `class multiprocessing.managers.BaseManager([address[, authkey]])`812813Создает объект BaseManager.814815После создания следует вызвать [`start()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или `get_server().serve_forever()`, чтобы гарантировать, что объект менеджера ссылается на запущенный процесс менеджера.816817*address* – это адрес, по которому процесс менеджера ожидает новые подключения. Если *address* равен `None`, то выбирается произвольный.818819*authkey* – это ключ аутентификации, который будет использоваться для проверки подлинности входящих подключений к процессу сервера. Если *authkey* равен `None`, то используется `current_process().authkey`. В противном случае используется *authkey*, и он должен быть строкой байтов.820821#### `start([initializer[, initargs]])`822823Запускает подпроцесс для запуска менеджера. Если *initializer* не равен `None`, то подпроцесс вызовет `initializer(*initargs)` при запуске.824825#### `get_server()`826827Возвращает объект `Server`, представляющий реальный сервер под управлением Manager. Объект `Server` поддерживает метод `serve_forever()`:828829```python830>>> from multiprocessing.managers import BaseManager831>>> manager = BaseManager(address=('', 50000), authkey=b'abc')832>>> server = manager.get_server()833>>> server.serve_forever()834```835836`Server` также имеет атрибут [`address`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).837838#### `connect()`839840Подключает локальный объект менеджера к удаленному процессу менеджера:841842```python843>>> from multiprocessing.managers import BaseManager844>>> m = BaseManager(address=('127.0.0.1', 5000), authkey=b'abc')845>>> m.connect()846```847848#### `shutdown()`849850Останавливает процесс, используемый менеджером. Это доступно только в том случае, если [`start()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.851852Этот метод можно вызывать несколько раз.853854#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`855856Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.857858*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.859860*callable* – это вызываемый объект, используемый для создания объектов для этого идентификатора типа. Если экземпляр менеджера будет создан с помощью метода класса `from_address()` или если аргумент *create\_method* равен `False`, то этот параметр можно оставить равным `None`.861862*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.863864*exposed* используется для указания последовательности имён методов, к которым прокси для этого typeid должны иметь доступ через `BaseProxy._callMethod()`. (Если *exposed* равен `None`, то используется `proxytype._exposed_`, если он существует.) В случае, когда список exposed не указан, все «открытые методы» разделяемого объекта будут доступны. (Здесь «открытый метод» означает любой атрибут, который имеет метод [`__call__()`](https://python-all.ru/3.2/reference/datamodel.html#object.__call__) и чьё имя не начинается с `'_'`.)865866*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно сопоставляет имена методов со строками typeid. (Если *method\_to\_typeid* равен `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом в этом отображении или если отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.867868*create\_method* определяет, должен ли быть создан метод с именем *typeid*, который можно использовать, чтобы сообщить серверному процессу о создании нового разделяемого объекта и возврате прокси для него. По умолчанию это `True`.869870Экземпляры [`BaseManager`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:871872#### `address`873874Адрес, используемый менеджером.875876#### `class multiprocessing.managers.SyncManager`877878Подкласс [`BaseManager`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.879880Он также поддерживает создание разделяемых списков и словарей.881882#### `BoundedSemaphore([value])`883884Создаёт разделяемый объект [`threading.BoundedSemaphore`](https://python-all.ru/3.2/library/threading.html#threading.BoundedSemaphore) и возвращает прокси для него.885886#### `Condition([lock])`887888Создаёт разделяемый объект [`threading.Condition`](https://python-all.ru/3.2/library/threading.html#threading.Condition) и возвращает прокси для него.889890Если *блокировка* указан, то он должен быть прокси для объекта [`threading.Lock`](https://python-all.ru/3.2/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/3.2/library/threading.html#threading.RLock).891892#### `Event()`893894Создаёт разделяемый объект [`threading.Event`](https://python-all.ru/3.2/library/threading.html#threading.Event) и возвращает для него прокси.895896#### `Lock()`897898Создаёт разделяемый объект [`threading.Lock`](https://python-all.ru/3.2/library/threading.html#threading.Lock) и возвращает для него прокси.899900#### `Namespace()`901902Создаёт разделяемый объект [`Namespace`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.SyncManager.Namespace) и возвращает для него прокси.903904#### `Queue([maxsize])`905906Создаёт разделяемый объект [`queue.Queue`](https://python-all.ru/3.2/library/queue.html#queue.Queue) и возвращает для него прокси.907908#### `RLock()`909910Создаёт разделяемый объект [`threading.RLock`](https://python-all.ru/3.2/library/threading.html#threading.RLock) и возвращает для него прокси.911912#### `Semaphore([value])`913914Создаёт разделяемый объект [`threading.Semaphore`](https://python-all.ru/3.2/library/threading.html#threading.Semaphore) и возвращает прокси для него.915916#### `Array(typecode, sequence)`917918Создать массив и вернуть для него прокси.919920#### `Value(typecode, value)`921922Создаёт объект с атрибутом `value`, доступным для записи, и возвращает прокси для него.923924#### `dict()`925926#### `dict(mapping)`927928#### `dict(sequence)`929930Создаёт разделяемый объект `dict` и возвращает для него прокси.931932#### `list()`933934#### `list(sequence)`935936Создаёт разделяемый объект `list` и возвращает для него прокси.937938> **Примечание**939>940> Изменения изменяемых значений или элементов в прокси-объектах словарей и списков не будут распространяться через менеджер, поскольку прокси не может отследить, когда его значения или элементы изменяются. Чтобы изменить такой элемент, можно переназначить изменённый объект обратно в прокси-контейнер:941>942> ```python943> # создать прокси для списка и добавить изменяемый объект (словарь)944> lproxy = manager.list()945> lproxy.append({})946> # теперь изменить словарь947> d = lproxy[0]948> d['a'] = 1949> d['b'] = 2950> # на данный момент изменения в d еще не синхронизированы, но при951> # при переприсваивании словаря прокси получает уведомление об изменении952> lproxy[0] = d953> ```954955#### 16.3.2.7.1. Объекты пространства имён956957Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.958959Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:960961```python962>>> manager = multiprocessing.Manager()963>>> Global = manager.Namespace()964>>> Global.x = 10965>>> Global.y = 'hello'966>>> Global._z = 12.3    # это атрибут прокси967>>> print(Global)968Namespace(x=10, y='hello')969```970971#### 16.3.2.7.2. Настраиваемые менеджеры972973Чтобы создать собственный менеджер, создаётся подкласс [`BaseManager`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseManager) и используется метод класса [`register()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:974975```python976from multiprocessing.managers import BaseManager977978class MathsClass:979    def add(self, x, y):980        return x + y981    def mul(self, x, y):982        return x * y983984class MyManager(BaseManager):985    pass986987MyManager.register('Maths', MathsClass)988989if __name__ == '__main__':990    manager = MyManager()991    manager.start()992    maths = manager.Maths()993    print(maths.add(4, 3))         # выводит 7994    print(maths.mul(7, 8))         # выводит 56995```996997#### 16.3.2.7.3. Использование удалённого менеджера998999Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).10001001Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:10021003```python1004>>> from multiprocessing.managers import BaseManager1005>>> import queue1006>>> queue = queue.Queue()1007>>> class QueueManager(BaseManager): pass1008>>> QueueManager.register('get_queue', callable=lambda:queue)1009>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1010>>> s = m.get_server()1011>>> s.serve_forever()1012```10131014Один клиент может получить доступ к серверу следующим образом:10151016```python1017>>> from multiprocessing.managers import BaseManager1018>>> class QueueManager(BaseManager): pass1019>>> QueueManager.register('get_queue')1020>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1021>>> m.connect()1022>>> queue = m.get_queue()1023>>> queue.put('hello')1024```10251026Другой клиент также может использовать его:10271028```python1029>>> from multiprocessing.managers import BaseManager1030>>> class QueueManager(BaseManager): pass1031>>> QueueManager.register('get_queue')1032>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1033>>> m.connect()1034>>> queue = m.get_queue()1035>>> queue.get()1036'hello'1037```10381039Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:10401041```python1042>>> from multiprocessing import Process, Queue1043>>> from multiprocessing.managers import BaseManager1044>>> class Worker(Process):1045...     def __init__(self, q):1046...         self.q = q1047...         super(Worker, self).__init__()1048...     def run(self):1049...         self.q.put('local hello')1050...1051>>> queue = Queue()1052>>> w = Worker(queue)1053>>> w.start()1054>>> class QueueManager(BaseManager): pass1055...1056>>> QueueManager.register('get_queue', callable=lambda: queue)1057>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1058>>> s = m.get_server()1059>>> s.serve_forever()1060```10611062### 16.3.2.8. Прокси-объекты10631064Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.10651066Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:10671068```python1069>>> from multiprocessing import Manager1070>>> manager = Manager()1071>>> l = manager.list([i*i for i in range(10)])1072>>> print(l)1073[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1074>>> print(repr(l))1075<ListProxy object, typeid 'list' at 0x...>1076>>> l[4]1077161078>>> l[2:5]1079[4, 9, 16]1080```10811082Обратите внимание, что применение [`str()`](https://python-all.ru/3.2/library/functions.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/3.2/library/functions.html#repr) вернёт представление прокси.10831084Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:10851086```python1087>>> a = manager.list()1088>>> b = manager.list()1089>>> a.append(b)         # референт a теперь содержит референт b1090>>> print(a, b)1091[[]] []1092>>> b.append('hello')1093>>> print(a, b)1094[['hello']] ['hello']1095```10961097> **Примечание**1098>1099> Типы прокси в [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) не поддерживают сравнение по значению. Так, например, имеем:1100>1101> ```python1102> >>> manager.list([1,2,3]) == [1,2,3]1103> False1104> ```1105>1106> При сравнении следует просто использовать копию референта.11071108#### `class multiprocessing.managers.BaseProxy`11091110Объекты прокси являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseProxy).11111112#### `_callmethod(methodname[, args[, kwds]])`11131114Вызывает метод референта прокси и возвращает результат.11151116Если `proxy` является прокси, чей референт – `obj`, то выражение11171118```python1119proxy._callmethod(methodname, args, kwds)1120```11211122вычислит выражение11231124```python1125getattr(obj, methodname)(*args, **kwds)1126```11271128в процессе менеджера.11291130Возвращаемое значение будет копией результата вызова или прокси на новый разделяемый объект – см. документацию по аргументу *method\_to\_typeid* метода [`BaseManager.register()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).11311132Если вызов порождает исключение, оно возбуждается повторно в [`_callmethod()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). Если в процессе менеджера возникло другое исключение, оно преобразуется в исключение `RemoteError` и возбуждается в [`_callmethod()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod).11331134Обратите внимание: если *methodname* не был *опубликован*, будет возбуждено исключение.11351136Пример использования [`_callmethod()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):11371138```python1139>>> l = manager.list(range(10))1140>>> l._callmethod('__len__')1141101142>>> l._callmethod('__getslice__', (2, 7))   # эквивалентно `l[2:7]`1143[2, 3, 4, 5, 6]1144>>> l._callmethod('__getitem__', (20,))     # эквивалентно `l[20]`1145Traceback (most recent call last):1146...1147IndexError: list index out of range1148```11491150#### `_getvalue()`11511152Возвращает копию референта.11531154Если референт не может быть сериализован (unpicklable), то это вызовет исключение.11551156#### `__repr__()`11571158Возвращает представление прокси-объекта.11591160#### `__str__()`11611162Возвращает представление референта.11631164#### 16.3.2.8.1. Очистка11651166Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.11671168Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.11691170### 16.3.2.9. Пулы процессов11711172Можно создать пул процессов, которые будут выполнять отправленные им задачи с помощью класса `Pool`.11731174#### `class multiprocessing.Pool([processes[, initializer[, initargs[, maxtasksperchild]]]])`11751176Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.11771178*processes* – количество используемых рабочих процессов. Если *processes* равно `None`, используется число, возвращаемое `cpu_count()`. Если *initializer* не `None`, каждый рабочий процесс при запуске вызовет `initializer(*initargs)`.11791180Новое в версии 3.2: *maxtasksperchild* – это количество задач, которое рабочий процесс может выполнить, прежде чем завершится и будет заменён новым рабочим процессом, чтобы освободить неиспользуемые ресурсы. Значение по умолчанию *maxtasksperchild* – None, что означает, что рабочие процессы будут жить столько же, сколько и пул.11811182> **Примечание**1183>1184> Рабочие процессы внутри `Pool` обычно существуют в течение всего времени работы очереди задач пула. Часто используемый в других системах (например, Apache, mod\_wsgi и т.п.) подход к освобождению ресурсов, удерживаемых рабочими процессами, состоит в том, чтобы разрешить рабочему процессу в пуле выполнить только заданный объём работы, после чего он завершается, очищается и порождается новый процесс для замены старого. Параметр *maxtasksperchild* в `Pool` предоставляет эту возможность конечному пользователю.11851186#### `apply(func[, args[, kwds]])`11871188Вызывает *func* с аргументами *args* и именованными аргументами *kwds*. Блокирует выполнение до получения результата. Поскольку этот метод блокирует, [`apply_async()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.apply_async) лучше подходит для параллельной работы. Кроме того, *func* выполняется только в одном из рабочих процессов пула.11891190#### `apply_async(func[, args[, kwds[, callback[, error_callback]]]])`11911192Вариант метода [`apply()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.apply), который возвращает объект результата.11931194Если *колбэк* указан, он должен быть вызываемым объектом, принимающим один аргумент. Когда результат готов, вызывается *колбэк*, если только вызов не завершился ошибкой; в этом случае вместо него вызывается *error\_callback*.11951196Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.11971198Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.11991200#### `map(func, iterable[, chunksize])`12011202Параллельный аналог встроенной функции [`map()`](https://python-all.ru/3.2/library/functions.html#map) (однако поддерживает только один аргумент *iterable*). Блокирует выполнение до получения результата.12031204Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.12051206#### `map_async(func, iterable[, chunksize[, callback[, error_callback]]])`12071208Вариант метода [`map()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map), который возвращает объект результата.12091210Если *колбэк* указан, он должен быть вызываемым объектом, принимающим один аргумент. Когда результат готов, вызывается *колбэк*, если только вызов не завершился ошибкой; в этом случае вместо него вызывается *error\_callback*.12111212Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.12131214Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.12151216#### `imap(func, iterable[, chunksize])`12171218Более ленивая версия [`map()`](https://python-all.ru/3.2/library/functions.html#map).12191220The *chunksize* argument is the same as the one used by the [`map()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map) method. For very long iterables using a large value for *chunksize* can make the job complete **much** faster than using the default value of `1`.12211222Кроме того, если *chunksize* равно `1`, то метод `next()` итератора, возвращаемого методом [`imap()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` возбудит `multiprocessing.TimeoutError`, если результат не может быть возвращён в течение *timeout* секунд.12231224#### `imap_unordered(func, iterable[, chunksize])`12251226То же, что и [`imap()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), но порядок результатов из возвращаемого итератора может быть произвольным. (Только при наличии одного рабочего процесса порядок гарантированно «правильный».)12271228#### `close()`12291230Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.12311232#### `terminate()`12331234Немедленно останавливает рабочие процессы, не завершая незавершённую работу. Когда объект пула собирается сборщиком мусора, [`terminate()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate) будет вызван немедленно.12351236#### `join()`12371238Ожидает завершения рабочих процессов. Перед вызовом [`join()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.join) необходимо вызвать [`close()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.close) или [`terminate()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate).12391240#### `class multiprocessing.pool.AsyncResult`12411242Класс результата, возвращаемого методами `Pool.apply_async()` и `Pool.map_async()`.12431244#### `get([timeout])`12451246Возвращает результат, когда он будет получен. Если *timeout* не равно `None` и результат не получен в течение *timeout* секунд, то возбуждается `multiprocessing.TimeoutError`. Если удалённый вызов возбудил исключение, то это исключение будет повторно возбуждено методом [`get()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get).12471248#### `wait([timeout])`12491250Ожидает, пока результат не станет доступен или пока не пройдёт *timeout* секунд.12511252#### `ready()`12531254Возвращает, завершён ли вызов.12551256#### `successful()`12571258Возвращает, завершился ли вызов без возбуждения исключения. Возбуждает [`AssertionError`](https://python-all.ru/3.2/library/exceptions.html#AssertionError), если результат ещё не готов.12591260Следующий пример демонстрирует использование пула:12611262```python1263from multiprocessing import Pool12641265def f(x):1266    return x*x12671268if __name__ == '__main__':1269    pool = Pool(processes=4)              # запустить 4 рабочих процесса12701271    result = pool.apply_async(f, (10,))   # асинхронно вычислить "f(10)"1272    print(result.get(timeout=1))          # выводит "100", если компьютер не *очень* медленный12731274    print(pool.map(f, range(10)))         # выводит "[0, 1, 4,..., 81]"12751276    it = pool.imap(f, range(10))1277    print(next(it))                       # выводит "0"1278    print(next(it))                       # выводит "1"1279    print(it.next(timeout=1))             # выводит "4", если компьютер не *очень* медленный12801281    import time1282    result = pool.apply_async(time.sleep, (10,))1283    print(result.get(timeout=1))          # вызывает TimeoutError1284```12851286### 16.3.2.10. Прослушиватели и клиенты12871288Обычно обмен сообщениями между процессами осуществляется с помощью очередей или с использованием объектов `Connection`, возвращаемых `Pipe()`.12891290Однако модуль [`multiprocessing.connection`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing.connection) обеспечивает дополнительную гибкость. Он, по сути, предоставляет высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows, а также поддерживает *аутентификацию по дайджесту* с использованием модуля [`hmac`](https://python-all.ru/3.2/library/hmac.html#module-hmac).12911292#### `multiprocessing.connection.deliver_challenge(connection, authkey)`12931294Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.12951296Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).12971298#### `multiprocessing.connection.answerChallenge(connection, authkey)`12991300Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.13011302Если приветственное сообщение не получено, то возникает исключение [`AuthenticationError`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).13031304#### `multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`13051306Пытается установить соединение с прослушивателем, который использует адрес *address*, и возвращает объект [`Connection`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Connection).13071308Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [*Address Formats*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-address-formats))13091310Если *authenticate* равно `True` или *authkey* является байтовой строкой, то используется аутентификация по дайджесту. Ключом для аутентификации будет либо *authkey*, либо `current_process().authkey`, если *authkey* равно `None`. Если аутентификация не удалась, то возникает исключение [`AuthenticationError`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.connection.AuthenticationError). См. [*Ключи аутентификации*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-auth-keys).13111312#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`13131314Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.13151316*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.13171318> **Примечание**1319>1320> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.13211322*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них только первый гарантированно доступен. Если *family* равен `None`, то семейство определяется по формату *address*. Если *address* также равен `None`, то выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [*Address Formats*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равен `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/3.2/library/tempfile.html#tempfile.mkstemp).13231324Если объект прослушивателя использует сокет, то *backlog* (по умолчанию 1) передаётся методу `listen()` сокета после его привязки.13251326Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.13271328Если *authkey* является байтовой строкой, то она будет использоваться в качестве ключа аутентификации; в противном случае она должна быть *None*.13291330Если *authkey* равно `None` и *authenticate* равно `True`, то в качестве ключа аутентификации используется `current_process().authkey`. Если *authkey* равно `None` и *authenticate* равно `False`, то аутентификация не производится. Если аутентификация не удалась, то возникает исключение [`AuthenticationError`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.connection.AuthenticationError). См. [*Ключи аутентификации*](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing-auth-keys).13311332#### `accept()`13331334Принимает соединение на привязанном сокете или именованном канале объекта прослушивателя и возвращает объект `Connection`. Если аутентификация запрошена и не удалась, то возникает исключение [`AuthenticationError`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).13351336#### `close()`13371338Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.13391340Объекты Listener имеют следующие свойства только для чтения:13411342#### `address`13431344Адрес, используемый объектом Listener.13451346#### `last_accepted`13471348Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то `None`.13491350Модуль определяет два исключения:13511352#### `exception multiprocessing.connection.AuthenticationError`13531354Исключение, возникающее при ошибке аутентификации.13551356**Примеры**13571358Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:13591360```python1361from multiprocessing.connection import Listener1362from array import array13631364address = ('localhost', 6000)     # семейство определяется как 'AF_INET'1365listener = Listener(address, authkey=b'secret password')13661367conn = listener.accept()1368print('connection accepted from', listener.last_accepted)13691370conn.send([2.25, None, 'junk', float])13711372conn.send_bytes(b'hello')13731374conn.send_bytes(array('i', [42, 1729]))13751376conn.close()1377listener.close()1378```13791380Следующий код подключается к серверу и получает от него некоторые данные:13811382```python1383from multiprocessing.connection import Client1384from array import array13851386address = ('localhost', 6000)1387conn = Client(address, authkey=b'secret password')13881389print(conn.recv())                  # => [2.25, None, 'junk', float]13901391print(conn.recv_bytes())            # => 'hello'13921393arr = array('i', [0, 0, 0, 0, 0])1394print(conn.recv_bytes_into(arr))    # => 81395print(arr)                          # => array('i', [42, 1729, 0, 0, 0])13961397conn.close()1398```13991400#### 16.3.2.10.1. Форматы адресов14011402- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.1403- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.1404- **Адрес `'AF_PIPE'` – это строка вида**14051406  `r'\\.\pipe\PipeName'`. Чтобы использовать [`Client()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.connection.Client) для подключения к именованному каналу на удалённом компьютере с именем *ServerName*, следует использовать адрес вида `r'\\ServerName\pipe\PipeName'` вместо.14071408Обратите внимание, что любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не адресом `'AF_UNIX'`.14091410### 16.3.2.11. Ключи аутентификации14111412При использовании `Connection.recv()` полученные данные автоматически распаковываются. К сожалению, распаковка данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/3.2/library/hmac.html#module-hmac) для аутентификации по дайджесту.14131414Ключ аутентификации – это байтовая строка, которую можно рассматривать как пароль: после установления соединения обе стороны требуют доказательства, что другая сторона знает ключ аутентификации. (Демонстрация того, что обе стороны используют один и тот же ключ, **не** предполагает отправку ключа по соединению.)14151416Если запрошена аутентификация, но ключ аутентификации не указан, то используется возвращаемое значение `current_process().authkey` (см. [`процесс`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process)). Это значение будет автоматически унаследовано любым объектом [`процесса`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.Process), который создаёт текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы будут использовать единый ключ аутентификации, который можно применять при установке соединений между ними.14171418Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/3.2/library/os.html#os.urandom).14191420### 16.3.2.12. Журналирование14211422Доступна некоторая поддержка логирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/3.2/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому возможно (в зависимости от типа обработчика) перемешивание сообщений от разных процессов.14231424#### `multiprocessing.get_logger()`14251426Возвращает регистратор (logger), используемый модулем [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing). При необходимости будет создан новый.14271428При первом создании регистратор имеет уровень `logging.NOTSET` и не имеет обработчика по умолчанию. Сообщения, отправленные этому регистратору, по умолчанию не распространяются на корневой регистратор.14291430Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.14311432#### `multiprocessing.log_to_stderr()`14331434Эта функция вызывает [`get_logger()`](https://python-all.ru/3.2/library/multiprocessing.html#multiprocessing.get_logger), но в дополнение к возврату регистратора, созданного get\_logger, она добавляет обработчик, который отправляет вывод в [`sys.stderr`](https://python-all.ru/3.2/library/sys.html#sys.stderr), используя формат `'[%(levelname)s/%(processName)s] %(message)s'`.14351436Ниже приведён пример сеанса с включённым журналированием:14371438```python1439>>> import multiprocessing, logging1440>>> logger = multiprocessing.log_to_stderr()1441>>> logger.setLevel(logging.INFO)1442>>> logger.warning('doomed')1443[WARNING/MainProcess] doomed1444>>> m = multiprocessing.Manager()1445[INFO/SyncManager-...] child process calling self.run()1446[INFO/SyncManager-...] created temp directory /.../pymp-...1447[INFO/SyncManager-...] manager serving at '/.../listener-...'1448>>> del m1449[INFO/MainProcess] sending shutdown message to manager1450[INFO/SyncManager-...] manager exiting with exitcode 01451```14521453В дополнение к этим двум функциям ведения журнала, multiprocessing также предоставляет два дополнительных атрибута уровня журналирования. Это `SUBWARNING` и `SUBDEBUG`. Приведенная ниже таблица показывает, где они располагаются в обычной иерархии уровней.14541455| Уровень | Числовое значение |1456| --- | --- |1457| `SUBWARNING` | 25 |1458| `SUBDEBUG` | 5 |14591460Полную таблицу уровней логирования см. в модуле [`logging`](https://python-all.ru/3.2/library/logging.html#module-logging).14611462Эти дополнительные уровни журналирования используются в основном для некоторых отладочных сообщений в модуле multiprocessing. Ниже приведен тот же пример, что и выше, но с включенным `SUBDEBUG`:14631464```python1465>>> import multiprocessing, logging1466>>> logger = multiprocessing.log_to_stderr()1467>>> logger.setLevel(multiprocessing.SUBDEBUG)1468>>> logger.warning('doomed')1469[WARNING/MainProcess] doomed1470>>> m = multiprocessing.Manager()1471[INFO/SyncManager-...] child process calling self.run()1472[INFO/SyncManager-...] created temp directory /.../pymp-...1473[INFO/SyncManager-...] manager serving at '/.../pymp-djGBXN/listener-...'1474>>> del m1475[SUBDEBUG/MainProcess] finalizer calling ...1476[INFO/MainProcess] sending shutdown message to manager1477[DEBUG/SyncManager-...] manager received shutdown message1478[SUBDEBUG/SyncManager-...] calling <Finalize object, callback=unlink, ...1479[SUBDEBUG/SyncManager-...] finalizer calling <built-in function unlink> ...1480[SUBDEBUG/SyncManager-...] calling <Finalize object, dead>1481[SUBDEBUG/SyncManager-...] finalizer calling <function rmtree at 0x5aa730> ...1482[INFO/SyncManager-...] manager exiting with exitcode 01483```14841485### 16.3.2.13. Модуль [`multiprocessing.dummy`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing.dummy)14861487[`multiprocessing.dummy`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing.dummy) воспроизводит API модуля [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing), но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/3.2/library/threading.html#module-threading).14881489## 16.3.3. Рекомендации по программированию14901491Существуют определённые рекомендации и идиомы, которые следует соблюдать при использовании [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing).14921493### 16.3.3.1. Все платформы14941495Избегайте разделяемого состояния14961497> По возможности следует стараться избегать передачи больших объёмов данных между процессами.1498>1499> Вероятно, лучше использовать очереди или каналы для связи между процессами, чем низкоуровневые примитивы синхронизации из модуля [`threading`](https://python-all.ru/3.2/library/threading.html#module-threading).15001501Сериализуемость15021503> Убедитесь, что аргументы методов прокси-объектов сериализуемы.15041505Потокобезопасность прокси15061507> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.1508>1509> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)15101511Присоединение процессов-зомби15121513> В Unix, когда процесс завершается, но к нему не был присоединён вызовом join, он становится зомби. Их не должно быть много, потому что каждый раз при запуске нового процесса (или вызове1514>1515> `active_children()`1516>1517> ) все завершённые процессы, к которым ещё не был применён join, будут присоединены. Также вызов1518>1519> `Process.is_alive()`1520>1521> для завершённого процесса присоединит его. Тем не менее, считается хорошей практикой явно присоединять все запущенные процессы.15221523Лучше наследовать, чем упаковывать/распаковывать15241525> В Windows многие типы из1526>1527> [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing)1528>1529> должны быть сериализуемы (picklable), чтобы дочерние процессы могли их использовать. Однако в целом следует избегать передачи общих объектов другим процессам через каналы (pipes) или очереди. Вместо этого нужно организовать программу так, чтобы процесс, которому требуется доступ к общему ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.15301531Избегайте завершения процессов15321533> Использование метода `Process.terminate()` для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), используемые в данный момент процессом, станут повреждёнными или недоступными для других процессов.1534>1535> Поэтому, вероятно, лучше рассматривать использование `Process.terminate()` только для процессов, которые никогда не используют разделяемые ресурсы.15361537Присоединение процессов, использующих очереди15381539> Имейте в виду, что процесс, поместивший элементы в очередь, будет ждать перед завершением, пока все буферизованные элементы не будут переданы потоком «feeder» в нижележащий канал. (Дочерний процесс может вызвать метод очереди `Queue.cancel_join_thread()`, чтобы избежать такого поведения.)1540>1541> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещенные в очередь, в конечном итоге будут удалены до того, как процесс будет присоединен (joined). В противном случае нельзя быть уверенным, что процессы, которые помещали элементы в очередь, завершатся. Также помните, что недемонические процессы будут присоединены автоматически.1542>1543> Пример, который приведёт к взаимоблокировке:1544>1545> ```python1546> from multiprocessing import Process, Queue1547>1548> def f(q):1549>     q.put('X' * 1000000)1550>1551> if __name__ == '__main__':1552>     queue = Queue()1553>     p = Process(target=f, args=(queue,))1554>     p.start()1555>     p.join()                    # это приводит к взаимоблокировке1556>     obj = queue.get()1557> ```1558>1559> Исправлением здесь была бы перестановка двух последних строк (или просто удаление строки с `p.join()`).15601561Явная передача ресурсов дочерним процессам15621563> В Unix дочерний процесс может использовать общий ресурс, созданный в родительском процессе, с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.1564>1565> Помимо обеспечения (потенциальной) совместимости с Windows, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если какой-либо ресурс освобождается при сборке мусора в родительском процессе.1566>1567> Так, например1568>1569> ```python1570> from multiprocessing import Process, Lock1571>1572> def f():1573>     ... do something using "lock" ...1574>1575> if __name__ == '__main__':1576>    lock = Lock()1577>    for i in range(10):1578>         Process(target=f).start()1579> ```1580>1581> следует переписать как1582>1583> ```python1584> from multiprocessing import Process, Lock1585>1586> def f(l):1587>     ... do something using "l" ...1588>1589> if __name__ == '__main__':1590>    lock = Lock()1591>    for i in range(10):1592>         Process(target=f, args=(lock,)).start()1593> ```15941595Остерегайтесь замены [`sys.stdin`](https://python-all.ru/3.2/library/sys.html#sys.stdin) на «объект, подобный файлу».15961597> [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) изначально безусловно вызывал:1598>1599> ```python1600> os.close(sys.stdin.fileno())1601> ```1602>1603> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:1604>1605> ```python1606> sys.stdin.close()1607> sys.stdin = open(os.devnull)1608> ```1609>1610> Это решает фундаментальную проблему конфликтов процессов, приводящих к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, заменяющих [`sys.stdin()`](https://python-all.ru/3.2/library/sys.html#sys.stdin) на «объект, подобный файлу» (file-like object) с буферизацией вывода. Опасность заключается в том, что если несколько процессов вызовут `close()` для этого объекта, одни и те же данные могут быть сброшены в объект несколько раз, что приведёт к повреждению данных.1611>1612> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:1613>1614> ```python1615> @property1616> def cache(self):1617>     pid = os.getpid()1618>     if pid != self._pid:1619>         self._pid = pid1620>         self._cache = []1621>     return self._cache1622> ```1623>1624> Дополнительную информацию см. в [issue 5155](https://python-all.ru/3.2/library/multiprocessing.html), [issue 5313](https://python-all.ru/3.2/library/multiprocessing.html) и [issue 5331](https://python-all.ru/3.2/library/multiprocessing.html).16251626### 16.3.3.2. Windows16271628Поскольку в Windows нет [`os.fork()`](https://python-all.ru/3.2/library/os.html#os.fork), существуют несколько дополнительных ограничений:16291630Упаковываемость16311632> Убедитесь, что все аргументы `Process.__init__()` являются сериализуемыми (picklable). Это означает, в частности, что связанные или несвязанные методы нельзя использовать напрямую в качестве аргумента `target` в Windows – просто определите функцию и используйте ее вместо этого.1633>1634> Кроме того, если вы создаёте подкласс `Process`, убедитесь, что экземпляры будут сериализуемы с помощью pickle при вызове метода `Process.start()`.16351636Глобальные переменные16371638> Имейте в виду, что если код, выполняемый в дочернем процессе, пытается получить доступ к глобальной переменной, то значение, которое он видит (если оно есть), может не совпадать со значением в родительском процессе на момент вызова `Process.start()`.1639>1640> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.16411642Безопасный импорт главного модуля16431644> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).1645>1646> Например, в Windows запуск следующего модуля завершится ошибкой [`RuntimeError`](https://python-all.ru/3.2/library/exceptions.html#RuntimeError):1647>1648> ```python1649> from multiprocessing import Process1650>1651> def foo():1652>     print('hello')1653>1654> p = Process(target=foo)1655> p.start()1656> ```1657>1658> Вместо этого следует защитить «точку входа» программы, используя конструкцию `if __name__ == '__main__':` следующим образом:1659>1660> ```python1661> from multiprocessing import Process, freeze_support1662>1663> def foo():1664>     print('hello')1665>1666> if __name__ == '__main__':1667>     freeze_support()1668>     p = Process(target=foo)1669>     p.start()1670> ```1671>1672> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не в замороженном виде.)1673>1674> Это позволяет новому порождённому интерпретатору Python безопасно импортировать модуль, а затем выполнить функцию `foo()` этого модуля.1675>1676> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.16771678## 16.3.4. Примеры16791680Демонстрация создания и использования настраиваемых менеджеров и прокси:16811682```python1683#1684# Этот модуль показывает, как использовать произвольные вызываемые объекты с подклассом `BaseManager`.1685# `BaseManager`.1686#1687# Авторское право (c) 2006-2008, R Oudkerk1688# Все права защищены.1689#16901691from multiprocessing import freeze_support1692from multiprocessing.managers import BaseManager, BaseProxy1693import operator16941695##16961697class Foo:1698    def f(self):1699        print('you called Foo.f()')1700    def g(self):1701        print('you called Foo.g()')1702    def _h(self):1703        print('you called Foo._h()')17041705# Простая функция-генератор1706def baz():1707    for i in range(10):1708        yield i*i17091710# Тип прокси для объектов-генераторов1711class GeneratorProxy(BaseProxy):1712    _exposed_ = ('next', '__next__')1713    def __iter__(self):1714        return self1715    def __next__(self):1716        return self._callmethod('next')1717    def __next__(self):1718        return self._callmethod('__next__')17191720# Функция для возврата модуля operator1721def get_operator_module():1722    return operator17231724##17251726class MyManager(BaseManager):1727    pass17281729# зарегистрировать класс Foo; сделать `f()` и `g()` доступными через прокси1730MyManager.register('Foo1', Foo)17311732# зарегистрировать класс Foo; сделать `g()` и `_h()` доступными через прокси1733MyManager.register('Foo2', Foo, exposed=('g', '_h'))17341735# зарегистрировать функцию-генератор baz; использовать `GeneratorProxy` для создания прокси1736MyManager.register('baz', baz, proxytype=GeneratorProxy)17371738# зарегистрировать get_operator_module(); сделать публичные функции доступными через прокси1739MyManager.register('operator', get_operator_module)17401741##17421743def test():1744    manager = MyManager()1745    manager.start()17461747    print('-' * 20)17481749    f1 = manager.Foo1()1750    f1.f()1751    f1.g()1752    assert not hasattr(f1, '_h')1753    assert sorted(f1._exposed_) == sorted(['f', 'g'])17541755    print('-' * 20)17561757    f2 = manager.Foo2()1758    f2.g()1759    f2._h()1760    assert not hasattr(f2, 'f')1761    assert sorted(f2._exposed_) == sorted(['g', '_h'])17621763    print('-' * 20)17641765    it = manager.baz()1766    for i in it:1767        print('<%d>' % i, end=' ')1768    print()17691770    print('-' * 20)17711772    op = manager.operator()1773    print('op.add(23, 45) =', op.add(23, 45))1774    print('op.pow(2, 94) =', op.pow(2, 94))1775    print('op.getslice(range(10), 2, 6) =', op.getslice(list(range(10)), 2, 6))1776    print('op.repeat(range(5), 3) =', op.repeat(list(range(5)), 3))1777    print('op._exposed_ =', op._exposed_)17781779##17801781if __name__ == '__main__':1782    freeze_support()1783    test()1784```17851786Использование `Pool`:17871788```python1789#1790# Тест класса `multiprocessing.Pool`1791#1792# Авторское право (c) 2006-2008, R Oudkerk1793# Все права защищены.1794#17951796import multiprocessing1797import time1798import random1799import sys18001801#1802# Функции, используемые тестовым кодом1803#18041805def calculate(func, args):1806    result = func(*args)1807    return '%s says that %s%s = %s' % (1808        multiprocessing.current_process().name,1809        func.__name__, args, result1810        )18111812def calculatestar(args):1813    return calculate(*args)18141815def mul(a, b):1816    time.sleep(0.5 * random.random())1817    return a * b18181819def plus(a, b):1820    time.sleep(0.5 * random.random())1821    return a + b18221823def f(x):1824    return 1.0 / (x - 5.0)18251826def pow3(x):1827    return x ** 318281829def noop(x):1830    pass18311832#1833# Тестовый код1834#18351836def test():1837    print('cpu_count() = %d\n' % multiprocessing.cpu_count())18381839    #1840    # Создать пул1841    #18421843    PROCESSES = 41844    print('Creating pool with %d processes\n' % PROCESSES)1845    pool = multiprocessing.Pool(PROCESSES)1846    print('pool = %s' % pool)1847    print()18481849    #1850    # Тесты1851    #18521853    TASKS = [(mul, (i, 7)) for i in range(10)] + \1854            [(plus, (i, 8)) for i in range(10)]18551856    results = [pool.apply_async(calculate, t) for t in TASKS]1857    imap_it = pool.imap(calculatestar, TASKS)1858    imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)18591860    print('Ordered results using pool.apply_async():')1861    for r in results:1862        print('\t', r.get())1863    print()18641865    print('Ordered results using pool.imap():')1866    for x in imap_it:1867        print('\t', x)1868    print()18691870    print('Unordered results using pool.imap_unordered():')1871    for x in imap_unordered_it:1872        print('\t', x)1873    print()18741875    print('Ordered results using pool.map() --- will block till complete:')1876    for x in pool.map(calculatestar, TASKS):1877        print('\t', x)1878    print()18791880    #1881    # Простые тесты производительности1882    #18831884    N = 1000001885    print('def pow3(x): return x**3')18861887    t = time.time()1888    A = list(map(pow3, range(N)))1889    print('\tmap(pow3, range(%d)):\n\t\t%s seconds' % \1890          (N, time.time() - t))18911892    t = time.time()1893    B = pool.map(pow3, range(N))1894    print('\tpool.map(pow3, range(%d)):\n\t\t%s seconds' % \1895          (N, time.time() - t))18961897    t = time.time()1898    C = list(pool.imap(pow3, range(N), chunksize=N//8))1899    print('\tlist(pool.imap(pow3, range(%d), chunksize=%d)):\n\t\t%s' \1900          ' seconds' % (N, N//8, time.time() - t))19011902    assert A == B == C, (len(A), len(B), len(C))1903    print()19041905    L = [None] * 10000001906    print('def noop(x): pass')1907    print('L = [None] * 1000000')19081909    t = time.time()1910    A = list(map(noop, L))1911    print('\tmap(noop, L):\n\t\t%s seconds' % \1912          (time.time() - t))19131914    t = time.time()1915    B = pool.map(noop, L)1916    print('\tpool.map(noop, L):\n\t\t%s seconds' % \1917          (time.time() - t))19181919    t = time.time()1920    C = list(pool.imap(noop, L, chunksize=len(L)//8))1921    print('\tlist(pool.imap(noop, L, chunksize=%d)):\n\t\t%s seconds' % \1922          (len(L)//8, time.time() - t))19231924    assert A == B == C, (len(A), len(B), len(C))1925    print()19261927    del A, B, C, L19281929    #1930    # Тестирование обработки ошибок1931    #19321933    print('Testing error handling:')19341935    try:1936        print(pool.apply(f, (5,)))1937    except ZeroDivisionError:1938        print('\tGot ZeroDivisionError as expected from pool.apply()')1939    else:1940        raise AssertionError('expected ZeroDivisionError')19411942    try:1943        print(pool.map(f, list(range(10))))1944    except ZeroDivisionError:1945        print('\tGot ZeroDivisionError as expected from pool.map()')1946    else:1947        raise AssertionError('expected ZeroDivisionError')19481949    try:1950        print(list(pool.imap(f, list(range(10)))))1951    except ZeroDivisionError:1952        print('\tGot ZeroDivisionError as expected from list(pool.imap())')1953    else:1954        raise AssertionError('expected ZeroDivisionError')19551956    it = pool.imap(f, list(range(10)))1957    for i in range(10):1958        try:1959            x = next(it)1960        except ZeroDivisionError:1961            if i == 5:1962                pass1963        except StopIteration:1964            break1965        else:1966            if i == 5:1967                raise AssertionError('expected ZeroDivisionError')19681969    assert i == 91970    print('\tGot ZeroDivisionError as expected from IMapIterator.next()')1971    print()19721973    #1974    # Тестирование тайм-аутов1975    #19761977    print('Testing ApplyResult.get() with timeout:', end=' ')1978    res = pool.apply_async(calculate, TASKS[0])1979    while 1:1980        sys.stdout.flush()1981        try:1982            sys.stdout.write('\n\t%s' % res.get(0.02))1983            break1984        except multiprocessing.TimeoutError:1985            sys.stdout.write('.')1986    print()1987    print()19881989    print('Testing IMapIterator.next() with timeout:', end=' ')1990    it = pool.imap(calculatestar, TASKS)1991    while 1:1992        sys.stdout.flush()1993        try:1994            sys.stdout.write('\n\t%s' % it.next(0.02))1995        except StopIteration:1996            break1997        except multiprocessing.TimeoutError:1998            sys.stdout.write('.')1999    print()2000    print()20012002    #2003    # Тестирование колбэка2004    #20052006    print('Testing callback:')20072008    A = []2009    B = [56, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]20102011    r = pool.apply_async(mul, (7, 8), callback=A.append)2012    r.wait()20132014    r = pool.map_async(pow3, list(range(10)), callback=A.extend)2015    r.wait()20162017    if A == B:2018        print('\tcallbacks succeeded\n')2019    else:2020        print('\t*** callbacks failed\n\t\t%s != %s\n' % (A, B))20212022    #2023    # Проверить, что нет незавершённых задач2024    #20252026    assert not pool._cache, 'cache = %r' % pool._cache20272028    #2029    # Проверить методы close()2030    #20312032    print('Testing close():')20332034    for worker in pool._pool:2035        assert worker.is_alive()20362037    result = pool.apply_async(time.sleep, [0.5])2038    pool.close()2039    pool.join()20402041    assert result.get() is None20422043    for worker in pool._pool:2044        assert not worker.is_alive()20452046    print('\tclose() succeeded\n')20472048    #2049    # Проверить метод terminate()2050    #20512052    print('Testing terminate():')20532054    pool = multiprocessing.Pool(2)2055    DELTA = 0.12056    ignore = pool.apply(pow3, [2])2057    results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]2058    pool.terminate()2059    pool.join()20602061    for worker in pool._pool:2062        assert not worker.is_alive()20632064    print('\tterminate() succeeded\n')20652066    #2067    # Проверить сборку мусора2068    #20692070    print('Testing garbage collection:')20712072    pool = multiprocessing.Pool(2)2073    DELTA = 0.12074    processes = pool._pool2075    ignore = pool.apply(pow3, [2])2076    results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]20772078    results = pool = None20792080    time.sleep(DELTA * 2)20812082    for worker in processes:2083        assert not worker.is_alive()20842085    print('\tgarbage collection succeeded\n')20862087if __name__ == '__main__':2088    multiprocessing.freeze_support()20892090    assert len(sys.argv) in (1, 2)20912092    if len(sys.argv) == 1 or sys.argv[1] == 'processes':2093        print(' Using processes '.center(79, '-'))2094    elif sys.argv[1] == 'threads':2095        print(' Using threads '.center(79, '-'))2096        import multiprocessing.dummy as multiprocessing2097    else:2098        print('Usage:\n\t%s [processes | threads]' % sys.argv[0])2099        raise SystemExit(2)21002101    test()2102```21032104Типы синхронизации, такие как блокировки, условия и очереди:21052106```python2107#2108# Тестовый файл для пакета `multiprocessing`2109#2110# Авторское право (c) 2006-2008, R Oudkerk2111# Все права защищены.2112#21132114import time2115import sys2116import random2117from queue import Empty21182119import multiprocessing               # может быть перезаписан21202121#### TEST_VALUE21222123def value_func(running, mutex):2124    random.seed()2125    time.sleep(random.random()*4)21262127    mutex.acquire()2128    print('\n\t\t\t' + str(multiprocessing.current_process()) + ' has finished')2129    running.value -= 12130    mutex.release()21312132def test_value():2133    TASKS = 102134    running = multiprocessing.Value('i', TASKS)2135    mutex = multiprocessing.Lock()21362137    for i in range(TASKS):2138        p = multiprocessing.Process(target=value_func, args=(running, mutex))2139        p.start()21402141    while running.value > 0:2142        time.sleep(0.08)2143        mutex.acquire()2144        print(running.value, end=' ')2145        sys.stdout.flush()2146        mutex.release()21472148    print()2149    print('No more running processes')21502151#### TEST_QUEUE21522153def queue_func(queue):2154    for i in range(30):2155        time.sleep(0.5 * random.random())2156        queue.put(i*i)2157    queue.put('STOP')21582159def test_queue():2160    q = multiprocessing.Queue()21612162    p = multiprocessing.Process(target=queue_func, args=(q,))2163    p.start()21642165    o = None2166    while o != 'STOP':2167        try:2168            o = q.get(timeout=0.3)2169            print(o, end=' ')2170            sys.stdout.flush()2171        except Empty:2172            print('TIMEOUT')21732174    print()21752176#### TEST_CONDITION21772178def condition_func(cond):2179    cond.acquire()2180    print('\t' + str(cond))2181    time.sleep(2)2182    print('\tchild is notifying')2183    print('\t' + str(cond))2184    cond.notify()2185    cond.release()21862187def test_condition():2188    cond = multiprocessing.Condition()21892190    p = multiprocessing.Process(target=condition_func, args=(cond,))2191    print(cond)21922193    cond.acquire()2194    print(cond)2195    cond.acquire()2196    print(cond)21972198    p.start()21992200    print('main is waiting')2201    cond.wait()2202    print('main has woken up')22032204    print(cond)2205    cond.release()2206    print(cond)2207    cond.release()22082209    p.join()2210    print(cond)22112212#### TEST_SEMAPHORE22132214def semaphore_func(sema, mutex, running):2215    sema.acquire()22162217    mutex.acquire()2218    running.value += 12219    print(running.value, 'tasks are running')2220    mutex.release()22212222    random.seed()2223    time.sleep(random.random()*2)22242225    mutex.acquire()2226    running.value -= 12227    print('%s has finished' % multiprocessing.current_process())2228    mutex.release()22292230    sema.release()22312232def test_semaphore():2233    sema = multiprocessing.Semaphore(3)2234    mutex = multiprocessing.RLock()2235    running = multiprocessing.Value('i', 0)22362237    processes = [2238        multiprocessing.Process(target=semaphore_func,2239                                args=(sema, mutex, running))2240        for i in range(10)2241        ]22422243    for p in processes:2244        p.start()22452246    for p in processes:2247        p.join()22482249#### TEST_JOIN_TIMEOUT22502251def join_timeout_func():2252    print('\tchild sleeping')2253    time.sleep(5.5)2254    print('\n\tchild terminating')22552256def test_join_timeout():2257    p = multiprocessing.Process(target=join_timeout_func)2258    p.start()22592260    print('waiting for process to finish')22612262    while 1:2263        p.join(timeout=1)2264        if not p.is_alive():2265            break2266        print('.', end=' ')2267        sys.stdout.flush()22682269#### TEST_EVENT22702271def event_func(event):2272    print('\t%r is waiting' % multiprocessing.current_process())2273    event.wait()2274    print('\t%r has woken up' % multiprocessing.current_process())22752276def test_event():2277    event = multiprocessing.Event()22782279    processes = [multiprocessing.Process(target=event_func, args=(event,))2280                 for i in range(5)]22812282    for p in processes:2283        p.start()22842285    print('main is sleeping')2286    time.sleep(2)22872288    print('main is setting event')2289    event.set()22902291    for p in processes:2292        p.join()22932294#### TEST_SHAREDVALUES22952296def sharedvalues_func(values, arrays, shared_values, shared_arrays):2297    for i in range(len(values)):2298        v = values[i][1]2299        sv = shared_values[i].value2300        assert v == sv23012302    for i in range(len(values)):2303        a = arrays[i][1]2304        sa = list(shared_arrays[i][:])2305        assert a == sa23062307    print('Tests passed')23082309def test_sharedvalues():2310    values = [2311        ('i', 10),2312        ('h', -2),2313        ('d', 1.25)2314        ]2315    arrays = [2316        ('i', list(range(100))),2317        ('d', [0.25 * i for i in range(100)]),2318        ('H', list(range(1000)))2319        ]23202321    shared_values = [multiprocessing.Value(id, v) for id, v in values]2322    shared_arrays = [multiprocessing.Array(id, a) for id, a in arrays]23232324    p = multiprocessing.Process(2325        target=sharedvalues_func,2326        args=(values, arrays, shared_values, shared_arrays)2327        )2328    p.start()2329    p.join()23302331    assert p.exitcode == 023322333####23342335def test(namespace=multiprocessing):2336    global multiprocessing23372338    multiprocessing = namespace23392340    for func in [test_value, test_queue, test_condition,2341                 test_semaphore, test_join_timeout, test_event,2342                 test_sharedvalues]:23432344        print('\n\t######## %s\n' % func.__name__)2345        func()23462347    ignore = multiprocessing.active_children()      # очистка всех старых процессов2348    if hasattr(multiprocessing, '_debug_info'):2349        info = multiprocessing._debug_info()2350        if info:2351            print(info)2352            raise ValueError('there should be no positive refcounts left')23532354if __name__ == '__main__':2355    multiprocessing.freeze_support()23562357    assert len(sys.argv) in (1, 2)23582359    if len(sys.argv) == 1 or sys.argv[1] == 'processes':2360        print(' Using processes '.center(79, '-'))2361        namespace = multiprocessing2362    elif sys.argv[1] == 'manager':2363        print(' Using processes and a manager '.center(79, '-'))2364        namespace = multiprocessing.Manager()2365        namespace.Process = multiprocessing.Process2366        namespace.current_process = multiprocessing.current_process2367        namespace.active_children = multiprocessing.active_children2368    elif sys.argv[1] == 'threads':2369        print(' Using threads '.center(79, '-'))2370        import multiprocessing.dummy as namespace2371    else:2372        print('Usage:\n\t%s [processes | manager | threads]' % sys.argv[0])2373        raise SystemExit(2)23742375    test(namespace)2376```23772378Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:23792380```python2381#2382# Простой пример использования пула рабочих процессов для выполнения задач.2383#2384# Обратите внимание, что результаты, скорее всего, не будут получены из выходной2385# очереди в том же порядке, в котором соответствующие задачи были2386# помещены во входную очередь. Если важно получить результаты2387# в исходном порядке, рассмотрите использование `Pool.map()` или2388# `Pool.imap()` (что в любом случае сократит объём необходимого кода).2389#2390# Авторское право (c) 2006-2008, R Oudkerk2391# Все права защищены.2392#23932394import time2395import random23962397from multiprocessing import Process, Queue, current_process, freeze_support23982399#2400# Функция, выполняемая рабочими процессами2401#24022403def worker(input, output):2404    for func, args in iter(input.get, 'STOP'):2405        result = calculate(func, args)2406        output.put(result)24072408#2409# Функция, используемая для вычисления результата2410#24112412def calculate(func, args):2413    result = func(*args)2414    return '%s says that %s%s = %s' % \2415        (current_process().name, func.__name__, args, result)24162417#2418# Функции, на которые ссылаются задачи2419#24202421def mul(a, b):2422    time.sleep(0.5*random.random())2423    return a * b24242425def plus(a, b):2426    time.sleep(0.5*random.random())2427    return a + b24282429#2430#2431#24322433def test():2434    NUMBER_OF_PROCESSES = 42435    TASKS1 = [(mul, (i, 7)) for i in range(20)]2436    TASKS2 = [(plus, (i, 8)) for i in range(10)]24372438    # Создание очередей2439    task_queue = Queue()2440    done_queue = Queue()24412442    # Отправка задач2443    for task in TASKS1:2444        task_queue.put(task)24452446    # Запуск рабочих процессов2447    for i in range(NUMBER_OF_PROCESSES):2448        Process(target=worker, args=(task_queue, done_queue)).start()24492450    # Получение и вывод результатов2451    print('Unordered results:')2452    for i in range(len(TASKS1)):2453        print('\t', done_queue.get())24542455    # Добавить ещё задач с помощью `put()`2456    for task in TASKS2:2457        task_queue.put(task)24582459    # Получить и вывести ещё несколько результатов2460    for i in range(len(TASKS2)):2461        print('\t', done_queue.get())24622463    # Сообщить дочерним процессам об остановке2464    for i in range(NUMBER_OF_PROCESSES):2465        task_queue.put('STOP')24662467if __name__ == '__main__':2468    freeze_support()2469    test()2470```24712472Пример того, как пул рабочих процессов может запускать по одному экземпляру [`SimpleHTTPRequestHandler`](https://python-all.ru/3.2/library/http.server.html#http.server.SimpleHTTPRequestHandler), разделяя один слушающий сокет.24732474```python2475#2476# Пример, в котором пул HTTP-серверов использует один общий слушающий сокет2477#2478# В Windows этот модуль зависит от возможности сериализации сокета с помощью pickle2479# объекта, чтобы рабочие процессы могли унаследовать копию сервера2480# объект. (Мы импортируем `multiprocessing.reduction`, чтобы включить эту сериализацию.)2481#2482# Не уверены, нужно ли синхронизировать доступ к методу `socket.accept()` с помощью2483# блокировки, разделяемой между процессами – похоже, это не обязательно.2484#2485# Авторское право (c) 2006-2008, R Oudkerk2486# Все права защищены.2487#24882489import os2490import sys24912492from multiprocessing import Process, current_process, freeze_support2493from http.server import HTTPServer2494from http.server import SimpleHTTPRequestHandler24952496if sys.platform == 'win32':2497    import multiprocessing.reduction    # сделать сокеты сериализуемыми/наследуемыми24982499def note(format, *args):2500    sys.stderr.write('[%s]\t%s\n' % (current_process().name, format % args))25012502class RequestHandler(SimpleHTTPRequestHandler):2503    # мы переопределяем log_message(), чтобы показывать, какой процесс обрабатывает запрос2504    def log_message(self, format, *args):2505        note(format, *args)25062507def serve_forever(server):2508    note('starting server')2509    try:2510        server.serve_forever()2511    except KeyboardInterrupt:2512        pass25132514def runpool(address, number_of_processes):2515    # создать единственный объект сервера – каждый дочерний процесс унаследует его копию2516    server = HTTPServer(address, RequestHandler)25172518    # создать дочерние процессы, которые будут работать как воркеры2519    for i in range(number_of_processes - 1):2520        Process(target=serve_forever, args=(server,)).start()25212522    # главный процесс также выступает в роли воркера2523    serve_forever(server)25242525def test():2526    DIR = os.path.join(os.path.dirname(__file__), '..')2527    ADDRESS = ('localhost', 8000)2528    NUMBER_OF_PROCESSES = 425292530    print('Serving at http://%s:%d using %d worker processes' % \2531          (ADDRESS[0], ADDRESS[1], NUMBER_OF_PROCESSES))2532    print('To exit press Ctrl-' + ['C', 'Break'][sys.platform=='win32'])25332534    os.chdir(DIR)2535    runpool(ADDRESS, NUMBER_OF_PROCESSES)25362537if __name__ == '__main__':2538    freeze_support()2539    test()2540```25412542Несколько простых тестов производительности, сравнивающих [`multiprocessing`](https://python-all.ru/3.2/library/multiprocessing.html#module-multiprocessing) с [`threading`](https://python-all.ru/3.2/library/threading.html#module-threading):25432544```python2545#2546# Простые тесты производительности для пакета multiprocessing2547#2548# Авторское право (c) 2006-2008, R Oudkerk2549# Все права защищены.2550#25512552import time2553import sys2554import multiprocessing2555import threading2556import queue2557import gc25582559if sys.platform == 'win32':2560    _timer = time.clock2561else:2562    _timer = time.time25632564delta = 125652566#### TEST_QUEUESPEED25672568def queuespeed_func(q, c, iterations):2569    a = '0' * 2562570    c.acquire()2571    c.notify()2572    c.release()25732574    for i in range(iterations):2575        q.put(a)25762577    q.put('STOP')25782579def test_queuespeed(Process, q, c):2580    elapsed = 02581    iterations = 125822583    while elapsed < delta:2584        iterations *= 225852586        p = Process(target=queuespeed_func, args=(q, c, iterations))2587        c.acquire()2588        p.start()2589        c.wait()2590        c.release()25912592        result = None2593        t = _timer()25942595        while result != 'STOP':2596            result = q.get()25972598        elapsed = _timer() - t25992600        p.join()26012602    print(iterations, 'objects passed through the queue in', elapsed, 'seconds')2603    print('average number/sec:', iterations/elapsed)26042605#### TEST_PIPESPEED26062607def pipe_func(c, cond, iterations):2608    a = '0' * 2562609    cond.acquire()2610    cond.notify()2611    cond.release()26122613    for i in range(iterations):2614        c.send(a)26152616    c.send('STOP')26172618def test_pipespeed():2619    c, d = multiprocessing.Pipe()2620    cond = multiprocessing.Condition()2621    elapsed = 02622    iterations = 126232624    while elapsed < delta:2625        iterations *= 226262627        p = multiprocessing.Process(target=pipe_func,2628                                    args=(d, cond, iterations))2629        cond.acquire()2630        p.start()2631        cond.wait()2632        cond.release()26332634        result = None2635        t = _timer()26362637        while result != 'STOP':2638            result = c.recv()26392640        elapsed = _timer() - t2641        p.join()26422643    print(iterations, 'objects passed through connection in',elapsed,'seconds')2644    print('average number/sec:', iterations/elapsed)26452646#### TEST_SEQSPEED26472648def test_seqspeed(seq):2649    elapsed = 02650    iterations = 126512652    while elapsed < delta:2653        iterations *= 226542655        t = _timer()26562657        for i in range(iterations):2658            a = seq[5]26592660        elapsed = _timer() - t26612662    print(iterations, 'iterations in', elapsed, 'seconds')2663    print('average number/sec:', iterations/elapsed)26642665#### TEST_LOCK26662667def test_lockspeed(l):2668    elapsed = 02669    iterations = 126702671    while elapsed < delta:2672        iterations *= 226732674        t = _timer()26752676        for i in range(iterations):2677            l.acquire()2678            l.release()26792680        elapsed = _timer() - t26812682    print(iterations, 'iterations in', elapsed, 'seconds')2683    print('average number/sec:', iterations/elapsed)26842685#### TEST_CONDITION26862687def conditionspeed_func(c, N):2688    c.acquire()2689    c.notify()26902691    for i in range(N):2692        c.wait()2693        c.notify()26942695    c.release()26962697def test_conditionspeed(Process, c):2698    elapsed = 02699    iterations = 127002701    while elapsed < delta:2702        iterations *= 227032704        c.acquire()2705        p = Process(target=conditionspeed_func, args=(c, iterations))2706        p.start()27072708        c.wait()27092710        t = _timer()27112712        for i in range(iterations):2713            c.notify()2714            c.wait()27152716        elapsed = _timer() - t27172718        c.release()2719        p.join()27202721    print(iterations * 2, 'waits in', elapsed, 'seconds')2722    print('average number/sec:', iterations * 2 / elapsed)27232724####27252726def test():2727    manager = multiprocessing.Manager()27282729    gc.disable()27302731    print('\n\t######## testing Queue.Queue\n')2732    test_queuespeed(threading.Thread, queue.Queue(),2733                    threading.Condition())2734    print('\n\t######## testing multiprocessing.Queue\n')2735    test_queuespeed(multiprocessing.Process, multiprocessing.Queue(),2736                    multiprocessing.Condition())2737    print('\n\t######## testing Queue managed by server process\n')2738    test_queuespeed(multiprocessing.Process, manager.Queue(),2739                    manager.Condition())2740    print('\n\t######## testing multiprocessing.Pipe\n')2741    test_pipespeed()27422743    print()27442745    print('\n\t######## testing list\n')2746    test_seqspeed(list(range(10)))2747    print('\n\t######## testing list managed by server process\n')2748    test_seqspeed(manager.list(list(range(10))))2749    print('\n\t######## testing Array("i", ..., lock=False)\n')2750    test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=False))2751    print('\n\t######## testing Array("i", ..., lock=True)\n')2752    test_seqspeed(multiprocessing.Array('i', list(range(10)), lock=True))27532754    print()27552756    print('\n\t######## testing threading.Lock\n')2757    test_lockspeed(threading.Lock())2758    print('\n\t######## testing threading.RLock\n')2759    test_lockspeed(threading.RLock())2760    print('\n\t######## testing multiprocessing.Lock\n')2761    test_lockspeed(multiprocessing.Lock())2762    print('\n\t######## testing multiprocessing.RLock\n')2763    test_lockspeed(multiprocessing.RLock())2764    print('\n\t######## testing lock managed by server process\n')2765    test_lockspeed(manager.Lock())2766    print('\n\t######## testing rlock managed by server process\n')2767    test_lockspeed(manager.RLock())27682769    print()27702771    print('\n\t######## testing threading.Condition\n')2772    test_conditionspeed(threading.Thread, threading.Condition())2773    print('\n\t######## testing multiprocessing.Condition\n')2774    test_conditionspeed(multiprocessing.Process, multiprocessing.Condition())2775    print('\n\t######## testing condition managed by a server process\n')2776    test_conditionspeed(multiprocessing.Process, manager.Condition())27772778    gc.enable()27792780if __name__ == '__main__':2781    multiprocessing.freeze_support()2782    test()2783```2784