multiprocessing.md
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