multiprocessing.md
1> **Источник:** https://python-all.ru/3.5/library/multiprocessing.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 17.2. [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) – параллелизм на основе процессов89**Исходный код:** [Lib/multiprocessing/](https://python-all.ru/src/3.5/Lib/multiprocessing)1011---1213## 17.2.1. Введение1415[`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) – это пакет, который поддерживает порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading). Пакет [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) обеспечивает как локальный, так и удалённый параллелизм, эффективно обходя [Глобальную блокировку интерпретатора](https://python-all.ru/3.5/glossary.html#term-global-interpreter-lock), используя подпроцессы вместо потоков. Благодаря этому модуль [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) позволяет программисту полностью использовать несколько процессоров на данной машине. Он работает как на Unix, так и на Windows.1617Модуль [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) также предоставляет API, у которых нет аналогов в модуле [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading). Яркий пример – объект [`Pool`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool), который предоставляет удобный способ распараллеливания выполнения функции для нескольких входных значений, распределяя входные данные между процессами (параллелизм данных). Следующий пример демонстрирует обычную практику определения таких функций в модуле, чтобы дочерние процессы могли успешно импортировать этот модуль. Этот базовый пример параллелизма данных с использованием [`Pool`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool),1819```python20from multiprocessing import Pool2122def f(x):23 return x*x2425if __name__ == '__main__':26 with Pool(5) as p:27 print(p.map(f, [1, 2, 3]))28```2930будет выводить на стандартный вывод3132```python33[1, 4, 9]34```3536### 17.2.1.1. Класс [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process)3738В [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) процессы порождаются созданием объекта [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.start). [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process) следует API [`threading.Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread). Простой пример многопроцессной программы:3940```python41from multiprocessing import Process4243def f(name):44 print('hello', name)4546if __name__ == '__main__':47 p = Process(target=f, args=('bob',))48 p.start()49 p.join()50```5152Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:5354```python55from multiprocessing import Process56import os5758def info(title):59 print(title)60 print('module name:', __name__)61 print('parent process:', os.getppid())62 print('process id:', os.getpid())6364def f(name):65 info('function f')66 print('hello', name)6768if __name__ == '__main__':69 info('main line')70 p = Process(target=f, args=('bob',))71 p.start()72 p.join()73```7475Объяснение того, зачем нужна часть `if __name__ == '__main__'`, см. в [руководстве по программированию](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-programming).7677### 17.2.1.2. Контексты и методы запуска7879В зависимости от платформы [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) поддерживает три способа запуска процесса. Эти *методы запуска*:8081> ***spawn***82>83> Родительский процесс запускает новый процесс интерпретатора Python. Дочерний процесс наследует только те ресурсы, которые необходимы для запуска метода [`run()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.run) объектов процесса. В частности, ненужные файловые дескрипторы и дескрипторы (handles) родительского процесса не наследуются. Запуск процесса с помощью этого метода довольно медленный по сравнению с использованием *fork* или *forkserver*.84>85> Доступен на Unix и Windows. По умолчанию на Windows.86>87> ***fork***88>89> Родительский процесс использует [`os.fork()`](https://python-all.ru/3.5/library/os.html#os.fork) для форка интерпретатора Python. Дочерний процесс в начале своего выполнения фактически идентичен родительскому процессу. Все ресурсы родительского процесса наследуются дочерним процессом. Обратите внимание, что безопасный форк многопоточного процесса проблематичен.90>91> Доступно только на Unix. По умолчанию используется на Unix.92>93> ***forkserver***94>95> Когда программа запускается и выбирает метод запуска *forkserver*, запускается серверный процесс. После этого каждый раз, когда требуется новый процесс, родительский процесс подключается к серверу и запрашивает создание (fork) нового процесса. Серверный процесс (fork server) является однопоточным, поэтому ему безопасно использовать [`os.fork()`](https://python-all.ru/3.5/library/os.html#os.fork). Ненужные ресурсы не наследуются.96>97> Доступно на платформах Unix, которые поддерживают передачу файловых дескрипторов через каналы Unix.9899Изменено в версии 3.4: *spawn* добавлен на всех платформах Unix, а *forkserver* добавлен для некоторых платформ Unix. Дочерние процессы больше не наследуют все наследуемые дескрипторы родительского процесса в Windows.100101В Unix при использовании методов запуска *spawn* или *forkserver* также будет запущен процесс *отслеживателя семафоров*, который отслеживает отсоединённые именованные семафоры, созданные процессами программы. Когда все процессы завершатся, отслеживатель семафоров отсоединяет все оставшиеся семафоры. Обычно их не должно быть, но если процесс был убит сигналом, могут остаться «утекшие» семафоры. (Отсоединение именованных семафоров – серьёзное дело, так как система допускает лишь ограниченное количество, и они не будут автоматически отсоединены до следующей перезагрузки.)102103Для выбора метода запуска используется [`set_start_method()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.set_start_method) в блоке `if __name__ == '__main__'` главного модуля. Например:104105```python106import multiprocessing as mp107108def foo(q):109 q.put('hello')110111if __name__ == '__main__':112 mp.set_start_method('spawn')113 q = mp.Queue()114 p = mp.Process(target=foo, args=(q,))115 p.start()116 print(q.get())117 p.join()118```119120[`set_start_method()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.set_start_method) не должен использоваться более одного раза в программе.121122В качестве альтернативы можно использовать [`get_context()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.get_context) для получения объекта контекста. Объекты контекста имеют тот же API, что и модуль multiprocessing, и позволяют использовать несколько методов запуска в одной программе.123124```python125import multiprocessing as mp126127def foo(q):128 q.put('hello')129130if __name__ == '__main__':131 ctx = mp.get_context('spawn')132 q = ctx.Queue()133 p = ctx.Process(target=foo, args=(q,))134 p.start()135 print(q.get())136 p.join()137```138139Обратите внимание, что объекты, относящиеся к одному контексту, могут быть несовместимы с процессами другого контекста. В частности, блокировки, созданные с помощью контекста *fork*, не могут быть переданы процессам, запущенным методами запуска *spawn* или *forkserver*.140141Библиотека, которая хочет использовать определённый метод запуска, вероятно, должна использовать [`get_context()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.get_context), чтобы не мешать выбору пользователя библиотеки.142143### 17.2.1.3. Обмен объектами между процессами144145[`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) поддерживает два типа каналов связи между процессами:146147**Очереди**148149> Класс [`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue) является почти точной копией [`queue.Queue`](https://python-all.ru/3.5/library/queue.html#queue.Queue). Например:150>151> ```python152> from multiprocessing import Process, Queue153>154> def f(q):155> q.put([42, None, 'hello'])156>157> if __name__ == '__main__':158> q = Queue()159> p = Process(target=f, args=(q,))160> p.start()161> print(q.get()) # печатает "[42, None, 'hello']"162> p.join()163> ```164>165> Очереди потокобезопасны и безопасны для процессов.166167**Каналы**168169> Функция [`Pipe()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является дуплексным (двунаправленным). Например:170>171> ```python172> from multiprocessing import Process, Pipe173>174> def f(conn):175> conn.send([42, None, 'hello'])176> conn.close()177>178> if __name__ == '__main__':179> parent_conn, child_conn = Pipe()180> p = Process(target=f, args=(child_conn,))181> p.start()182> print(parent_conn.recv()) # печатает "[42, None, 'hello']"183> p.join()184> ```185>186> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы [`send()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.send) и [`recv()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.recv) (среди прочих). Обратите внимание: данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.187188### 17.2.1.4. Синхронизация между процессами189190[`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) содержит эквиваленты всех примитивов синхронизации из [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс одновременно выводит на стандартный вывод:191192```python193from multiprocessing import Process, Lock194195def f(l, i):196 l.acquire()197 try:198 print('hello world', i)199 finally:200 l.release()201202if __name__ == '__main__':203 lock = Lock()204205 for num in range(10):206 Process(target=f, args=(lock, num)).start()207```208209Без использования блокировки вывод от разных процессов может полностью перепутаться.210211### 17.2.1.5. Совместное использование состояния между процессами212213Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.214215Однако если действительно необходимо использовать некоторые общие данные, то [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) предоставляет несколько способов сделать это.216217**Разделяемая память**218219> Данные могут храниться в отображении разделяемой памяти с помощью [`Value`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Array). Например, следующий код220>221> ```python222> from multiprocessing import Process, Value, Array223>224> def f(n, a):225> n.value = 3.1415927226> for i in range(len(a)):227> a[i] = -a[i]228>229> if __name__ == '__main__':230> num = Value('d', 0.0)231> arr = Array('i', range(10))232>233> p = Process(target=f, args=(num, arr))234> p.start()235> p.join()236>237> print(num.value)238> print(arr[:])239> ```240>241> выведет242>243> ```python244> 3.1415927245> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]246> ```247>248> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, аналогичные используемым модулем [`array`](https://python-all.ru/3.5/library/array.html#module-array): `'d'` означает число с плавающей запятой двойной точности, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасными для процессов и потоков.249>250> Для большей гибкости при работе с разделяемой памятью можно использовать модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing.sharedctypes), который поддерживает создание произвольных объектов ctypes, выделенных из разделяемой памяти.251252**Серверный процесс**253254> Объект-менеджер, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими с помощью прокси.255>256> Менеджер, возвращаемый `Manager()`, поддерживает типы [`list`](https://python-all.ru/3.5/library/stdtypes.html#list), [`dict`](https://python-all.ru/3.5/library/stdtypes.html#dict), [`Namespace`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.Namespace), [`Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Event), [`Barrier`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Barrier), [`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Array). Например,257>258> ```python259> from multiprocessing import Process, Manager260>261> def f(d, l):262> d[1] = '1'263> d['2'] = 2264> d[0.25] = None265> l.reverse()266>267> if __name__ == '__main__':268> with Manager() as manager:269> d = manager.dict()270> l = manager.list(range(10))271>272> p = Process(target=f, args=(d, l))273> p.start()274> p.join()275>276> print(d)277> print(l)278> ```279>280> выведет281>282> ```python283> {0.25: None, 1: '1', '2': 2}284> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]285> ```286>287> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.288289### 17.2.1.6. Использование пула рабочих процессов290291Класс [`Pool`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool) представляет пул рабочих процессов. Он имеет методы, которые позволяют передавать задачи рабочим процессам несколькими различными способами.292293Например:294295```python296from multiprocessing import Pool, TimeoutError297import time298import os299300def f(x):301 return x*x302303if __name__ == '__main__':304 # запустить 4 рабочих процесса305 with Pool(processes=4) as pool:306307 # печатает "[0, 1, 4,..., 81]"308 print(pool.map(f, range(10)))309310 # печатает те же числа в произвольном порядке311 for i in pool.imap_unordered(f, range(10)):312 print(i)313314 # вычисляет "f(20)" асинхронно315 res = pool.apply_async(f, (20,)) # выполняется *только* в одном процессе316 print(res.get(timeout=1)) # печатает "400"317318 # вычисляет "os.getpid()" асинхронно319 res = pool.apply_async(os.getpid, ()) # выполняется *только* в одном процессе320 print(res.get(timeout=1)) # печатает PID этого процесса321322 # запуск нескольких асинхронных вычислений *может* задействовать больше процессов323 multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]324 print([res.get(timeout=1) for res in multiple_results])325326 # заставить один рабочий процесс спать 10 секунд327 res = pool.apply_async(time.sleep, (10,))328 try:329 print(res.get(timeout=1))330 except TimeoutError:331 print("We lacked patience and got a multiprocessing.TimeoutError")332333 print("For the moment, the pool remains available for more work")334335 # выход из блока 'with' остановил пул336 print("Now the pool is closed and no longer available")337```338339Обратите внимание: методы пула должны использоваться только тем процессом, который его создал.340341> **Примечание**342>343> Для функциональности этого пакета требуется, чтобы модуль `__main__` был импортируемым дочерними процессами. Это описано в [Рекомендациях по программированию](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-programming), но стоит упомянуть об этом и здесь. Это означает, что некоторые примеры, такие как примеры [`multiprocessing.pool.Pool`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool), не будут работать в интерактивном интерпретаторе. Например:344>345> ```python346> >>> from multiprocessing import Pool347> >>> p = Pool(5)348> >>> def f(x):349> ... return x*x350> ...351> >>> p.map(f, [1,2,3])352> Process PoolWorker-1:353> Process PoolWorker-2:354> Process PoolWorker-3:355> Traceback (most recent call last):356> Traceback (most recent call last):357> Traceback (most recent call last):358> AttributeError: 'module' object has no attribute 'f'359> AttributeError: 'module' object has no attribute 'f'360> AttributeError: 'module' object has no attribute 'f'361> ```362>363> (Если попробовать это сделать, на самом деле будут выведены три полные трассировки стека, перемешанные в полу-случайном порядке, и затем, возможно, придётся как-то остановить главный процесс.)364365## 17.2.2. Справочник366367Пакет [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) в основном повторяет API модуля [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading).368369### 17.2.2.1. [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process) и исключения370371#### `class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)`372373Объекты Process представляют активность, выполняемую в отдельном процессе. Класс [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process) имеет эквиваленты всех методов [`threading.Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread).374375Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должен быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию `None`, то есть ничего не вызывается. *name* – это имя процесса (см. [`name`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.name) для подробностей). *args* – кортеж аргументов для вызова целевой функции. *kwargs* – это словарь именованных аргументов для вызова целевой функции. Если указан, аргумент только по ключевому слову *daemon* устанавливает флаг процесса [`daemon`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.daemon) в `True` или `False`. Если `None` (по умолчанию), этот флаг будет унаследован от создающего процесса.376377По умолчанию в *target* не передаётся никаких аргументов.378379Если подкласс переопределяет конструктор, он должен вызывать конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.380381Изменено в версии 3.3: Добавлен аргумент *daemon*.382383#### `run()`384385Метод, представляющий активность процесса.386387Этот метод можно переопределить в подклассе. Стандартный метод [`run()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.388389#### `start()`390391Запускает активность процесса.392393Этот метод должен быть вызван не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.394395#### `join([timeout])`396397Если необязательный аргумент *timeout* равен `None` (по умолчанию), метод блокируется до завершения процесса, у которого вызывается метод [`join()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.join). Если *timeout* – положительное число, блокировка длится не более *timeout* секунд. Обратите внимание: метод возвращает `None`, если процесс завершился или истекло время ожидания. Проверьте [`exitcode`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.exitcode) процесса, чтобы определить, завершился ли он.398399Процесс может быть присоединён много раз.400401Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.402403#### `name`404405Имя процесса. Имя – это строка, используемая только для идентификации. Оно не имеет семантического значения. Несколько процессов могут иметь одно и то же имя.406407Начальное имя устанавливается конструктором. Если явное имя не передано конструктору, создаётся имя вида ‘Process-N1:N2:…:Nk’, где каждая Nk – это N-й дочерний процесс своего родителя.408409#### `is_alive()`410411Возвращает, жив ли процесс.412413Грубо говоря, объект процесса живёт с момента возврата метода [`start()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.414415#### `daemon`416417Флаг демона процесса (логическое значение). Должен быть установлен до вызова [`start()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.start).418419Начальное значение наследуется от создающего процесса.420421Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.422423Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.424425В дополнение к API [`threading.Thread`](https://python-all.ru/3.5/library/threading.html#threading.Thread) объекты [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process) также поддерживают следующие атрибуты и методы:426427#### `pid`428429Возвращает идентификатор процесса. До запуска процесса это будет `None`.430431#### `exitcode`432433Код завершения дочернего процесса. Он будет равен `None`, если процесс ещё не завершён. Отрицательное значение *-N* означает, что дочерний процесс был завершён сигналом *N*.434435#### `authkey`436437Ключ аутентификации процесса (байтовая строка).438439При инициализации [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) главному процессу назначается случайная строка с помощью [`os.urandom()`](https://python-all.ru/3.5/library/os.html#os.urandom).440441При создании объекта [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process) он наследует ключ аутентификации своего родительского процесса, хотя его можно изменить, установив [`authkey`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.authkey) в другую байтовую строку.442443См. [Ключи аутентификации](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-auth-keys).444445#### `sentinel`446447Числовой дескриптор системного объекта, который становится «готов» по завершении процесса.448449Это значение можно использовать для ожидания нескольких событий одновременно с помощью [`multiprocessing.connection.wait()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.connection.wait). В противном случае вызов [`join()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.join) проще.450451В Windows это дескриптор ОС, который можно использовать с семейством `WaitForSingleObject` и `WaitForMultipleObjects` вызовов API. В Unix это файловый дескриптор, который можно использовать с примитивами из модуля [`select`](https://python-all.ru/3.5/library/select.html#module-select).452453Новое в версии 3.3.454455#### `terminate()`456457Завершает процесс. На Unix это делается с помощью сигнала `SIGTERM`; на Windows используется `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.д. выполняться не будут.458459Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.460461> **Предупреждение**462>463> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.464465Обратите внимание: методы [`start()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.is_alive), [`terminate()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.terminate) и [`exitcode`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.exitcode) должны вызываться только тем процессом, который создал объект процесса.466467Пример использования некоторых методов [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process):468469```pycon470>>> import multiprocessing, time, signal471>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))472>>> print(p, p.is_alive())473<Process(Process-1, initial)> False474>>> p.start()475>>> print(p, p.is_alive())476<Process(Process-1, started)> True477>>> p.terminate()478>>> time.sleep(0.1)479>>> print(p, p.is_alive())480<Process(Process-1, stopped[SIGTERM])> False481>>> p.exitcode == -signal.SIGTERM482True483```484485#### `exception multiprocessing.ProcessError`486487Базовый класс всех исключений [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing).488489#### `exception multiprocessing.BufferTooShort`490491Исключение, возбуждаемое [`Connection.recv_bytes_into()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.recv_bytes_into), когда предоставленный буфер слишком мал для чтения сообщения.492493Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде байтовой строки.494495#### `exception multiprocessing.AuthenticationError`496497Возбуждается при ошибке аутентификации.498499#### `exception multiprocessing.TimeoutError`500501Возбуждается методами с тайм-аутом по истечении тайм-аута.502503### 17.2.2.2. Каналы и очереди504505При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.506507Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает несколько производителей и потребителей).508509Типы [`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue), [`SimpleQueue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.SimpleQueue) и [`JoinableQueue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue) – это очереди FIFO с несколькими производителями и потребителями, основанные на классе [`queue.Queue`](https://python-all.ru/3.5/library/queue.html#queue.Queue) из стандартной библиотеки. Они отличаются тем, что в [`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue) отсутствуют методы [`task_done()`](https://python-all.ru/3.5/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.5/library/queue.html#queue.Queue.join), появившиеся в классе [`queue.Queue`](https://python-all.ru/3.5/library/queue.html#queue.Queue) в Python 2.5.510511Если используется [`JoinableQueue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue), то **обязательно** вызывайте [`JoinableQueue.task_done()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди; в противном случае семафор, используемый для подсчёта незавершённых задач, может переполниться, что приведёт к возбуждению исключения.512513Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [Менеджеры](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-managers).514515> **Примечание**516>517> [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) использует обычные исключения [`queue.Empty`](https://python-all.ru/3.5/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.5/library/queue.html#queue.Full) для сигнализации тайм-аута. Они недоступны в пространстве имён [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing), поэтому их необходимо импортировать из [`queue`](https://python-all.ru/3.5/library/queue.html#module-queue).518519> **Примечание**520>521> Когда объект помещается в очередь, он сериализуется с помощью модуля pickle, и фоновый поток позднее сбрасывает сериализованные данные в нижележащий канал. Это имеет некоторые неожиданные последствия, но обычно не вызывает практических трудностей – если это действительно беспокоит, можно вместо этого использовать очередь, созданную с помощью [менеджера](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-managers).522>523> 1. После помещения объекта в пустую очередь может пройти бесконечно малая задержка, прежде чем метод [`empty()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.empty) очереди вернёт [`False`](https://python-all.ru/3.5/library/constants.html#False) и [`get_nowait()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.get_nowait) сможет вернуться без возбуждения [`queue.Empty`](https://python-all.ru/3.5/library/queue.html#queue.Empty).524> 2. Если несколько процессов помещают объекты в очередь, возможно получение объектов на другом конце не по порядку. Однако объекты, помещённые в очередь одним и тем же процессом, всегда будут идти друг за другом в ожидаемом порядке.525526> **Предупреждение**527>528> Если процесс завершается принудительно с помощью [`Process.terminate()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/3.5/library/os.html#os.kill) в то время, как он пытается использовать [`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue), данные в очереди могут быть повреждены. Это может привести к тому, что любой другой процесс при попытке использовать очередь получит исключение.529530> **Предупреждение**531>532> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не использовал [`JoinableQueue.cancel_join_thread`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread)), он не завершится, пока все буферизованные элементы не будут сброшены в канал.533>534> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.535>536> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [Рекомендации по программированию](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-programming).537538Пример использования очередей для межпроцессного взаимодействия см. в разделе [Примеры](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-examples).539540#### `multiprocessing.Pipe([duplex])`541542Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection), представляющих концы канала.543544Если *duplex* равно `True` (по умолчанию), то канал двунаправленный. Если *duplex* равно `False`, то канал однонаправленный: `conn1` можно использовать только для получения сообщений, а `conn2` – только для отправки.545546#### `class multiprocessing.Queue([maxsize])`547548Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.549550Обычные исключения [`queue.Empty`](https://python-all.ru/3.5/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3.5/library/queue.html#queue.Full) из модуля [`queue`](https://python-all.ru/3.5/library/queue.html#module-queue) стандартной библиотеки возбуждаются для сигнализации тайм-аутов.551552[`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue) реализует все методы [`queue.Queue`](https://python-all.ru/3.5/library/queue.html#queue.Queue), за исключением [`task_done()`](https://python-all.ru/3.5/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3.5/library/queue.html#queue.Queue.join).553554#### `qsize()`555556Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.557558Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/3.5/library/exceptions.html#NotImplementedError) на Unix-платформах, таких как Mac OS X, где `sem_getvalue()` не реализован.559560#### `empty()`561562Возвращает `True`, если очередь пуста, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.563564#### `full()`565566Возвращает `True`, если очередь заполнена, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.567568#### `put(obj[, block[, timeout]])`569570Помещает obj в очередь. Если необязательный аргумент *block* равен `True` (по умолчанию) и *timeout* равен `None` (по умолчанию), при необходимости блокируется до появления свободного места. Если *timeout* положительное число, блокируется не более чем на *timeout* секунд и возбуждает исключение [`queue.Full`](https://python-all.ru/3.5/library/queue.html#queue.Full), если за это время свободное место не появилось. В противном случае (*block* равен `False`) помещает элемент в очередь, если свободное место доступно немедленно, иначе возбуждает исключение [`queue.Full`](https://python-all.ru/3.5/library/queue.html#queue.Full) (*timeout* в этом случае игнорируется).571572#### `put_nowait(obj)`573574Эквивалентно `put(obj, False)`.575576#### `get([block[, timeout]])`577578Удаляет и возвращает элемент из очереди. Если необязательный аргумент *block* равен `True` (по умолчанию) и *timeout* равен `None` (по умолчанию), при необходимости блокируется до появления элемента. Если *timeout* положительное число, блокируется не более чем на *timeout* секунд и возбуждает исключение [`queue.Empty`](https://python-all.ru/3.5/library/queue.html#queue.Empty), если за это время элемент не появился. В противном случае (block равен `False`) возвращает элемент, если он доступен немедленно, иначе возбуждает исключение [`queue.Empty`](https://python-all.ru/3.5/library/queue.html#queue.Empty) (*timeout* в этом случае игнорируется).579580#### `get_nowait()`581582Эквивалентно `get(False)`.583584[`multiprocessing.Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue) имеет несколько дополнительных методов, отсутствующих в [`queue.Queue`](https://python-all.ru/3.5/library/queue.html#queue.Queue). Обычно эти методы не нужны в большинстве кода:585586#### `close()`587588Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.589590#### `join_thread()`591592Присоединяет фоновый поток. Этот метод можно использовать только после вызова [`close()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.close). Он блокируется до завершения фонового потока, гарантируя, что все данные из буфера сброшены в канал.593594По умолчанию, если процесс не является создателем очереди, при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы сделать [`join_thread()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.join_thread) бездействующим.595596#### `cancel_join_thread()`597598Предотвращает блокировку [`join_thread()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.join_thread). В частности, это предотвращает автоматическое присоединение фонового потока при завершении процесса – см. [`join_thread()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.join_thread).599600Более подходящим именем для этого метода могло бы быть `allow_exit_without_flush()`. Он, скорее всего, приведёт к потере поставленных в очередь данных, и почти наверняка вам не понадобится его использовать. Он нужен только в том случае, если текущий процесс должен немедленно завершиться, не дожидаясь сброса поставленных в очередь данных в нижележащий канал, и вас не волнует потеря данных.601602> **Примечание**603>604> Функциональность этого класса требует работающей реализации общего семафора в хост-операционной системе. Без неё функциональность класса будет отключена, а попытки создать экземпляр [`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue) приведут к [`ImportError`](https://python-all.ru/3.5/library/exceptions.html#ImportError). См. [bpo-3770](https://python-all.ru/3.5/library/multiprocessing.html) для дополнительной информации. То же самое относится и ко всем специализированным типам очередей, перечисленным ниже.605606#### `class multiprocessing.SimpleQueue`607608Это упрощённый тип [`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue), очень похожий на [`Pipe`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Pipe) с блокировкой.609610#### `empty()`611612Возвращает `True`, если очередь пуста, иначе `False`.613614#### `get()`615616Извлекает и возвращает элемент из очереди.617618#### `put(item)`619620Помещает *item* в очередь.621622#### `class multiprocessing.JoinableQueue([maxsize])`623624[`JoinableQueue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue), подкласс [`Queue`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно имеет методы [`task_done()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue.join).625626#### `task_done()`627628Указывает, что ранее поставленная в очередь задача завершена. Используется потребителями очереди. Для каждого [`get()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.get), используемого для получения задачи, последующий вызов [`task_done()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) сообщает очереди, что обработка задачи завершена.629630Если [`join()`](https://python-all.ru/3.5/library/queue.html#queue.Queue.join) в данный момент заблокирован, он возобновится, когда все элементы будут обработаны (то есть для каждого элемента, который был [`put()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.put) в очередь, был получен вызов [`task_done()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done)).631632Вызывает [`ValueError`](https://python-all.ru/3.5/library/exceptions.html#ValueError), если вызывается больше раз, чем было помещено элементов в очередь.633634#### `join()`635636Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.637638Счётчик незавершённых задач увеличивается каждый раз, когда элемент добавляется в очередь. Счётчик уменьшается, когда потребитель вызывает [`task_done()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы указать, что элемент извлечён и вся работа над ним завершена. Когда счётчик незавершённых задач падает до нуля, [`join()`](https://python-all.ru/3.5/library/queue.html#queue.Queue.join) разблокируется.639640### 17.2.2.3. Разное641642#### `multiprocessing.active_children()`643644Возвращает список всех активных дочерних процессов текущего процесса.645646Вызов этой функции имеет побочный эффект – «присоединение» всех уже завершённых процессов.647648#### `multiprocessing.cpu_count()`649650Возвращает количество процессоров в системе. Может вызывать [`NotImplementedError`](https://python-all.ru/3.5/library/exceptions.html#NotImplementedError).651652> **См. также**653>654> [`os.cpu_count()`](https://python-all.ru/3.5/library/os.html#os.cpu_count)655656#### `multiprocessing.current_process()`657658Возвращает объект [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.659660Аналог [`threading.current_thread()`](https://python-all.ru/3.5/library/threading.html#threading.current_thread).661662#### `multiprocessing.freeze_support()`663664Добавляет поддержку для случая, когда программа, использующая [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing), была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)665666Эту функцию необходимо вызвать сразу после строки `if __name__ == '__main__'` в главном модуле. Например:667668```python669from multiprocessing import Process, freeze_support670671def f():672 print('hello world!')673674if __name__ == '__main__':675 freeze_support()676 Process(target=f).start()677```678679Если строка `freeze_support()` опущена, то попытка запустить замороженный исполняемый файл вызовет [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError).680681Вызов `freeze_support()` не имеет эффекта при запуске на любой операционной системе, кроме Windows. Кроме того, если модуль запускается обычным образом интерпретатором Python в Windows (программа не была заморожена), то `freeze_support()` также не имеет эффекта.682683#### `multiprocessing.get_all_start_methods()`684685Возвращает список поддерживаемых методов запуска, первый из которых является методом по умолчанию. Возможными методами запуска являются `'fork'`, `'spawn'` и `'forkserver'`. В Windows доступен только `'spawn'`. В Unix `'fork'` и `'spawn'` всегда поддерживаются, причём `'fork'` используется по умолчанию.686687Новое в версии 3.4.688689#### `multiprocessing.get_context(method=None)`690691Возвращает объект контекста, который имеет те же атрибуты, что и модуль [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing).692693Если *method* равен `None`, то возвращается контекст по умолчанию. В противном случае *method* должен быть `'fork'`, `'spawn'`, `'forkserver'`. [`ValueError`](https://python-all.ru/3.5/library/exceptions.html#ValueError) вызывается, если указанный метод запуска недоступен.694695Новое в версии 3.4.696697#### `multiprocessing.get_start_method(allow_none=False)`698699Возвращает имя метода запуска, используемого для запуска процессов.700701Если метод запуска не был задан и *allow\_none* равен false, то метод запуска фиксируется как используемый по умолчанию, и возвращается его название. Если метод запуска не был задан и *allow\_none* равен true, то возвращается `None`.702703Возвращаемое значение может быть `'fork'`, `'spawn'`, `'forkserver'` или `None`. `'fork'` – значение по умолчанию на Unix, а `'spawn'` – значение по умолчанию на Windows.704705Новое в версии 3.4.706707#### `multiprocessing.set_executable()`708709Устанавливает путь к интерпретатору Python, используемому при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/3.5/library/sys.html#sys.executable)). Встраивающим пользователям, вероятно, потребуется сделать что-то вроде710711```python712set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))713```714715прежде чем они смогут создавать дочерние процессы.716717Изменено в версии 3.4: Теперь поддерживается в Unix при использовании метода запуска `'spawn'`.718719#### `multiprocessing.set_start_method(method)`720721Задаёт метод, который будет использоваться для запуска дочерних процессов. *method* может быть `'fork'`, `'spawn'` или `'forkserver'`.722723Обратите внимание, что эта функция должна вызываться не более одного раза, и она должна быть защищена внутри блока `if __name__ == '__main__'` главного модуля.724725Новое в версии 3.4.726727> **Примечание**728>729> [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) не содержит аналогов [`threading.active_count()`](https://python-all.ru/3.5/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/3.5/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/3.5/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/3.5/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/3.5/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/3.5/library/threading.html#threading.local).730731### 17.2.2.4. Объекты соединений732733Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.734735Объекты соединения обычно создаются с помощью [`Pipe()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Pipe) – см. также [Слушатели и клиенты](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-listeners-clients).736737#### `class multiprocessing.Connection`738739#### `send(obj)`740741Отправляет объект на другой конец подключения; его следует читать с помощью [`recv()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.recv).742743Объект должен быть сериализуемым. Очень большие сериализованные объекты (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/3.5/library/exceptions.html#ValueError).744745#### `recv()`746747Возвращает объект, отправленный с другого конца подключения с помощью [`send()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.send). Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/3.5/library/exceptions.html#EOFError), если больше нечего получать и другой конец был закрыт.748749#### `fileno()`750751Возвращает файловый дескриптор или дескриптор (handle), используемый подключением.752753#### `close()`754755Закрывает подключение.756757Вызывается автоматически при сборке мусора для подключения.758759#### `poll([timeout])`760761Возвращает, есть ли данные для чтения.762763Если *timeout* не указан, то метод возвращается немедленно. Если *timeout* – число, то оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, то используется бесконечное ожидание.764765Обратите внимание: с помощью [`multiprocessing.connection.wait()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.connection.wait) можно одновременно опрашивать несколько объектов подключения.766767#### `send_bytes(buffer[, offset[, size]])`768769Отправляет байтовые данные из [байтоподобного объекта](https://python-all.ru/3.5/glossary.html#term-bytes-like-object) как полное сообщение.770771Если указан *offset*, то данные читаются с этой позиции в *buffer*. Если указан *size*, то из буфера будет прочитано соответствующее количество байт. Очень большие буферы (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/3.5/library/exceptions.html#ValueError)772773#### `recv_bytes([maxlength])`774775Возвращает полное сообщение из байтовых данных, отправленное с другого конца подключения, в виде строки. Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/3.5/library/exceptions.html#EOFError), если больше нечего получать и другой конец закрыт.776777Если указан *maxlength* и сообщение длиннее *maxlength*, то вызывается [`OSError`](https://python-all.ru/3.5/library/exceptions.html#OSError), и подключение больше не будет доступно для чтения.778779Изменено в версии 3.3: Ранее эта функция вызывала [`IOError`](https://python-all.ru/3.5/library/exceptions.html#IOError), которая теперь является псевдонимом [`OSError`](https://python-all.ru/3.5/library/exceptions.html#OSError).780781#### `recv_bytes_into(buffer[, offset])`782783Читает в *buffer* полное сообщение с байтовыми данными, отправленное с другого конца соединения, и возвращает количество байтов в сообщении. Блокирует выполнение, пока не появится что-то для приёма. Возбуждает [`EOFError`](https://python-all.ru/3.5/library/exceptions.html#EOFError), если больше нечего принимать и другой конец был закрыт.784785*buffer* должен быть доступным для записи [байтоподобным объектом](https://python-all.ru/3.5/glossary.html#term-bytes-like-object). Если задан *offset*, то сообщение будет записано в буфер, начиная с этой позиции. Offset должен быть неотрицательным целым числом, меньшим длины *buffer* (в байтах).786787Если буфер слишком мал, то вызывается исключение [`BufferTooShort`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.BufferTooShort) и полное сообщение доступно как `e.args[0]`, где `e` экземпляр исключения.788789Изменено в версии 3.3: Теперь объекты подключения могут передаваться между процессами с помощью [`Connection.send()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.send) и [`Connection.recv()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.recv).790791Новое в версии 3.3: Объекты Connection теперь поддерживают протокол управления контекстом – см. [Типы менеджеров контекста](https://python-all.ru/3.5/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__enter__) возвращает объект соединения, а [`__exit__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__exit__) вызывает [`close()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.close).792793Например:794795```pycon796>>> from multiprocessing import Pipe797>>> a, b = Pipe()798>>> a.send([1, 'hello', None])799>>> b.recv()800[1, 'hello', None]801>>> b.send_bytes(b'thank you')802>>> a.recv_bytes()803b'thank you'804>>> import array805>>> arr1 = array.array('i', range(5))806>>> arr2 = array.array('i', [0] * 10)807>>> a.send_bytes(arr1)808>>> count = b.recv_bytes_into(arr2)809>>> assert count == len(arr1) * arr1.itemsize810>>> arr2811array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])812```813814> **Предупреждение**815>816> Метод [`Connection.recv()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.recv) автоматически десериализует полученные данные, что может быть угрозой безопасности, если нельзя доверять процессу, отправившему сообщение.817>818> Поэтому, если объект подключения не был создан с помощью [`Pipe()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Pipe) следует использовать методы [`recv()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.recv) и [`send()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.send) только после выполнения некоторой аутентификации. См. [Ключи аутентификации](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-auth-keys).819820> **Предупреждение**821>822> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.823824### 17.2.2.5. Примитивы синхронизации825826В целом, в многопроцессных программах примитивы синхронизации требуются не так часто, как в многопоточных. См. документацию модуля [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading).827828Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [Менеджеры](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-managers).829830#### `class multiprocessing.Barrier(parties[, action[, timeout]])`831832Объект барьера: клон [`threading.Barrier`](https://python-all.ru/3.5/library/threading.html#threading.Barrier).833834Новое в версии 3.3.835836#### `class multiprocessing.BoundedSemaphore([value])`837838Объект ограниченного семафора: близкий аналог [`threading.BoundedSemaphore`](https://python-all.ru/3.5/library/threading.html#threading.BoundedSemaphore).839840Имеется единственное отличие от его близкого аналога: первый аргумент метода `acquire` называется *block*, что согласуется с [`Lock.acquire()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock.acquire).841842> **Примечание**843>844> На Mac OS X это неотличимо от [`Semaphore`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализован на этой платформе.845846#### `class multiprocessing.Condition([lock])`847848Условная переменная: псевдоним для [`threading.Condition`](https://python-all.ru/3.5/library/threading.html#threading.Condition).849850Если указана *блокировка*, то она должна быть объектом [`Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock) из [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing).851852Изменено в версии 3.3: Был добавлен метод [`wait_for()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait_for).853854#### `class multiprocessing.Event`855856Клон [`threading.Event`](https://python-all.ru/3.5/library/threading.html#threading.Event).857858#### `class multiprocessing.Lock`859860Нерекурсивный объект блокировки: близкий аналог [`threading.Lock`](https://python-all.ru/3.5/library/threading.html#threading.Lock). Когда процесс или поток захватывает блокировку, последующие попытки захватить её из любого процесса или потока будут блокироваться до её освобождения; любой процесс или поток может её освободить. Концепции и поведение [`threading.Lock`](https://python-all.ru/3.5/library/threading.html#threading.Lock) применительно к потокам воспроизводятся здесь в [`multiprocessing.Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock) применительно к процессам или потокам, за исключением отмеченных случаев.861862Обратите внимание, что [`Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.Lock`, инициализированный контекстом по умолчанию.863864[`Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock) поддерживает протокол [менеджера контекста](https://python-all.ru/3.5/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/3.5/reference/compound_stmts.html#with).865866#### `acquire(block=True, timeout=None)`867868Захватывает блокировку, блокирующую или неблокирующую.869870Если аргумент *block* установлен в `True` (по умолчанию), вызов метода будет блокироваться, пока блокировка не окажется в разблокированном состоянии, затем установит её в заблокированное состояние и вернёт `True`. Обратите внимание, что имя этого первого аргумента отличается от имени в [`threading.Lock.acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire).871872Если аргумент *block* установлен в `False`, вызов метода не блокируется. Если блокировка в данный момент находится в заблокированном состоянии, возвращается `False`; в противном случае блокировка устанавливается в заблокированное состояние и возвращается `True`.873874При вызове с положительным значением с плавающей точкой для *timeout* блокировка выполняется не дольше числа секунд, указанного в *timeout*, пока блокировку не удастся захватить. Вызовы с отрицательным значением *timeout* эквивалентны *timeout*, равному нулю. Вызовы со значением *timeout* равным `None` (по умолчанию) устанавливают бесконечный период ожидания. Обратите внимание, что обработка отрицательных или `None` значений для *timeout* отличается от реализованного поведения в [`threading.Lock.acquire()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.acquire). Аргумент *timeout* не имеет практического значения, если аргумент *block* установлен в `False`, и поэтому игнорируется. Возвращает `True`, если блокировка была захвачена, или `False`, если период ожидания истёк.875876#### `release()`877878Освобождает блокировку. Может вызываться из любого процесса или потока, а не только из того, который изначально захватил блокировку.879880Поведение аналогично [`threading.Lock.release()`](https://python-all.ru/3.5/library/threading.html#threading.Lock.release) за исключением того, что при вызове на незаблокированной блокировке возбуждается [`ValueError`](https://python-all.ru/3.5/library/exceptions.html#ValueError).881882#### `class multiprocessing.RLock`883884Объект рекурсивной блокировки: близкий аналог [`threading.RLock`](https://python-all.ru/3.5/library/threading.html#threading.RLock). Рекурсивная блокировка должна быть освобождена тем же процессом или потоком, который её захватил. После того как процесс или поток захватил рекурсивную блокировку, тот же процесс или поток может захватить её снова без блокировки; этот процесс или поток должен освободить её по одному разу за каждый захват.885886Обратите внимание, что [`RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.RLock`, инициализированный контекстом по умолчанию.887888[`RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock) поддерживает протокол [менеджера контекста](https://python-all.ru/3.5/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/3.5/reference/compound_stmts.html#with).889890#### `acquire(block=True, timeout=None)`891892Захватывает блокировку, блокирующую или неблокирующую.893894При вызове с аргументом *block*, установленным в `True`, блокируется до тех пор, пока блокировка не окажется в разблокированном состоянии (не принадлежит ни одному процессу или потоку), если только блокировка уже не принадлежит текущему процессу или потоку. Затем текущий процесс или поток получает владение блокировкой (если ещё не владеет ею), и уровень рекурсии внутри блокировки увеличивается на единицу, возвращая значение `True`. Обратите внимание, что имеется несколько различий в поведении этого первого аргумента по сравнению с реализацией [`threading.RLock.acquire()`](https://python-all.ru/3.5/library/threading.html#threading.RLock.acquire), начиная с названия самого аргумента.895896При вызове с аргументом *block*, установленным в `False`, не блокируется. Если блокировка уже была захвачена (и, следовательно, принадлежит) другим процессом или потоком, текущий процесс или поток не получает владение, а уровень рекурсии внутри блокировки не изменяется, возвращая значение `False`. Если блокировка находится в разблокированном состоянии, текущий процесс или поток получает владение, а уровень рекурсии увеличивается, возвращая значение `True`.897898Использование и поведение аргумента *timeout* такие же, как в [`Lock.acquire()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock.acquire). Обратите внимание, что некоторые из этих особенностей *timeout* отличаются от реализованного поведения в [`threading.RLock.acquire()`](https://python-all.ru/3.5/library/threading.html#threading.RLock.acquire).899900#### `release()`901902Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения уровень рекурсии становится нулевым, переводит блокировку в разблокированное состояние (не принадлежит ни одному процессу или потоку), и если какие-либо другие процессы или потоки ожидают разблокировки, позволяет ровно одному из них продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся заблокированной и принадлежащей вызывающему процессу или потоку.903904Вызывайте этот метод только когда вызывающий процесс или поток владеет блокировкой. Исключение [`AssertionError`](https://python-all.ru/3.5/library/exceptions.html#AssertionError) возбуждается, если этот метод вызывается процессом или потоком, не являющимся владельцем, или если блокировка находится в разблокированном (непринадлежащем) состоянии. Обратите внимание, что тип возбуждаемого исключения в этой ситуации отличается от реализованного поведения в [`threading.RLock.release()`](https://python-all.ru/3.5/library/threading.html#threading.RLock.release).905906#### `class multiprocessing.Semaphore([value])`907908Объект семафора: близкий аналог [`threading.Semaphore`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore).909910Имеется единственное отличие от его близкого аналога: первый аргумент метода `acquire` называется *block*, что согласуется с [`Lock.acquire()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock.acquire).911912> **Примечание**913>914> На Mac OS X `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с тайм-аутом будет эмулировать поведение этой функции с помощью спящего цикла.915916> **Примечание**917>918> Если сигнал SIGINT, сгенерированный `Ctrl-C`, поступает, когда главный поток блокирован вызовом `BoundedSemaphore.acquire()`, [`Lock.acquire()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock.acquire), [`RLock.acquire()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock.acquire), `Semaphore.acquire()`, `Condition.acquire()` или `Condition.wait()`, то вызов будет немедленно прерван и возбуждено [`KeyboardInterrupt`](https://python-all.ru/3.5/library/exceptions.html#KeyboardInterrupt).919>920> Это отличается от поведения [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading), где SIGINT игнорируется, пока выполняются эквивалентные блокирующие вызовы.921922> **Примечание**923>924> Некоторые функции этого пакета требуют работающей реализации общего семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки импортировать его приведут к [`ImportError`](https://python-all.ru/3.5/library/exceptions.html#ImportError). См. [bpo-3770](https://python-all.ru/3.5/library/multiprocessing.html) для получения дополнительной информации.925926### 17.2.2.6. Разделяемые [`ctypes`](https://python-all.ru/3.5/library/ctypes.html#module-ctypes) объекты927928Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.929930#### `multiprocessing.Value(typecode_or_type, *args, lock=True)`931932Возвращает объект [`ctypes`](https://python-all.ru/3.5/library/ctypes.html#module-ctypes), выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для объекта. Сам объект можно получить через атрибут *value* объекта [`Value`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Value).933934*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.5/library/array.html#module-array). *\*args* передаётся конструктору типа.935936Если *блокировка* равна `True` (по умолчанию), то создаётся новый рекурсивный объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".937938Операции, подобные `+=`, которые включают чтение и запись, не являются атомарными. Поэтому, если, например, требуется атомарно увеличить разделяемое значение, недостаточно просто сделать939940```python941counter.value += 1942```943944При условии, что связанная блокировка рекурсивна (по умолчанию она такой и является), можно вместо этого сделать945946```python947with counter.get_lock():948 counter.value += 1949```950951Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.952953#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`954955Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.956957*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо ctypes-тип, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.5/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.958959Если *блокировка* равен `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* – объект [`Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равен `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».960961Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.962963Обратите внимание, что массив типа [`ctypes.c_char`](https://python-all.ru/3.5/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.964965#### 17.2.2.6.1. Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing.sharedctypes)966967Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing.sharedctypes) предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/3.5/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.968969> **Примечание**970>971> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.972973#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`974975Возвращает массив ctypes, выделенный из разделяемой памяти.976977*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.5/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.978979Обратите внимание, что установка и получение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.980981#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`982983Возвращает объект ctypes, выделенный из разделяемой памяти.984985*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3.5/library/array.html#module-array). *\*args* передаётся конструктору типа.986987Обратите внимание, что установка и получение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.988989Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3.5/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют использовать его для сохранения и извлечения строк – см. документацию к [`ctypes`](https://python-all.ru/3.5/library/ctypes.html#module-ctypes).990991#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *, lock=True)`992993То же, что и [`RawArray()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного массива ctypes.994995Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".996997Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.998999#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args, lock=True)`10001001То же, что и [`RawValue()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного объекта ctypes.10021003Если *блокировка* равен `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* – объект [`Lock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равен `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».10041005Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.10061007#### `multiprocessing.sharedctypes.copy(obj)`10081009Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes *obj*.10101011#### `multiprocessing.sharedctypes.synchronized(obj[, lock])`10121013Возвращает процесс-безопасную обёртку для объекта ctypes, которая использует *блокировка* для синхронизации доступа. Если *блокировка* равен `None` (по умолчанию), объект [`multiprocessing.RLock`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.RLock) создаётся автоматически.10141015Синхронизированная обёртка будет иметь два дополнительных метода (помимо методов обёрнутого объекта): `get_obj()` возвращает обёрнутый объект, а `get_lock()` – объект блокировки, используемый для синхронизации.10161017Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.10181019Изменено в версии 3.5: Синхронизированные объекты поддерживают протокол [контекстного менеджера](https://python-all.ru/3.5/glossary.html#term-context-manager).10201021В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некоторый подкласс [`ctypes.Structure`](https://python-all.ru/3.5/library/ctypes.html#ctypes.Structure).)10221023| ctypes | sharedctypes с типом | sharedctypes с typecode |1024| --- | --- | --- |1025| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |1026| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) | |1027| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |1028| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |10291030Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:10311032```python1033from multiprocessing import Process, Lock1034from multiprocessing.sharedctypes import Value, Array1035from ctypes import Structure, c_double10361037class Point(Structure):1038 _fields_ = [('x', c_double), ('y', c_double)]10391040def modify(n, x, s, A):1041 n.value **= 21042 x.value **= 21043 s.value = s.value.upper()1044 for a in A:1045 a.x **= 21046 a.y **= 210471048if __name__ == '__main__':1049 lock = Lock()10501051 n = Value('i', 7)1052 x = Value(c_double, 1.0/3.0, lock=False)1053 s = Array('c', b'hello world', lock=lock)1054 A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)10551056 p = Process(target=modify, args=(n, x, s, A))1057 p.start()1058 p.join()10591060 print(n.value)1061 print(x.value)1062 print(s.value)1063 print([(a.x, a.y) for a in A])1064```10651066Выводятся следующие результаты:10671068```text10694910700.11111111111111111071HELLO WORLD1072[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]1073```10741075### 17.2.2.7. Менеджеры10761077Менеджеры предоставляют способ создания данных, которыми можно обмениваться между разными процессами, в том числе по сети между процессами, выполняющимися на разных машинах. Объект менеджера управляет серверным процессом, который управляет *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.10781079#### `multiprocessing.Manager()`10801081Возвращает запущенный объект [`SyncManager`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.SyncManager), который можно использовать для совместного использования объектов между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.10821083Процессы менеджера завершаются, как только они будут собраны сборщиком мусора или завершится их родительский процесс. Классы менеджера определены в модуле [`multiprocessing.managers`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing.managers):10841085#### `class multiprocessing.managers.BaseManager([address[, authkey]])`10861087Создает объект BaseManager.10881089После создания необходимо вызвать [`start()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или `get_server().serve_forever()`, чтобы объект менеджера ссылался на запущенный процесс менеджера.10901091*address* – адрес, на котором процесс менеджера ожидает новые подключения. Если *address* равно `None`, то выбирается произвольный адрес.10921093*authkey* – ключ аутентификации, который будет использоваться для проверки подлинности входящих подключений к серверному процессу. Если *authkey* равно `None`, используется `current_process().authkey`. В противном случае используется *authkey*, и он должен быть байтовой строкой.10941095#### `start([initializer[, initargs]])`10961097Запускает подпроцесс для запуска менеджера. Если *initializer* не равно `None`, то подпроцесс вызовет `initializer(*initargs)` при запуске.10981099#### `get_server()`11001101Возвращает объект `Server`, представляющий реальный сервер, управляемый менеджером. Объект `Server` поддерживает метод `serve_forever()`:11021103```python1104>>> from multiprocessing.managers import BaseManager1105>>> manager = BaseManager(address=('', 50000), authkey=b'abc')1106>>> server = manager.get_server()1107>>> server.serve_forever()1108```11091110`Server` также имеет атрибут [`address`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).11111112#### `connect()`11131114Подключает локальный объект менеджера к удаленному процессу менеджера:11151116```python1117>>> from multiprocessing.managers import BaseManager1118>>> m = BaseManager(address=('127.0.0.1', 5000), authkey=b'abc')1119>>> m.connect()1120```11211122#### `shutdown()`11231124Останавливает процесс, используемый менеджером. Это доступно только если [`start()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.11251126Этот метод можно вызывать несколько раз.11271128#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`11291130Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.11311132*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.11331134*callable* – это вызываемый объект, используемый для создания объектов для данного идентификатора типа. Если экземпляр менеджера будет подключен к серверу с помощью метода [`connect()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager.connect), или если аргумент *create\_method* равен `False`, то этот параметр можно оставить как `None`.11351136*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.11371138*exposed* используется для указания последовательности имен методов, к которым прокси для данного typeid должны иметь доступ через [`BaseProxy._callmethod()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). (Если *exposed* равно `None`, то вместо него используется `proxytype._exposed_`, если он существует.) Если список exposed не указан, то все «публичные методы» разделяемого объекта будут доступны. (Здесь «публичный метод» означает любой атрибут, у которого есть метод [`__call__()`](https://python-all.ru/3.5/reference/datamodel.html#object.__call__) и имя которого не начинается с `'_'`.)11391140*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно отображает имена методов в строки typeid. (Если *method\_to\_typeid* равно `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом этого отображения или отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.11411142*create\_method* определяет, следует ли создать метод с именем *typeid*, который можно использовать, чтобы указать серверному процессу создать новый разделяемый объект и вернуть для него прокси. По умолчанию `True`.11431144Экземпляры [`BaseManager`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:11451146#### `address`11471148Адрес, используемый менеджером.11491150Изменено в версии 3.3: Объекты Manager поддерживают протокол управления контекстом – см. [Типы менеджеров контекста](https://python-all.ru/3.5/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__enter__) запускает серверный процесс (если он еще не запущен), а затем возвращает объект менеджера. [`__exit__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__exit__) вызывает [`shutdown()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager.shutdown).11511152В предыдущих версиях [`__enter__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__enter__) не запускал серверный процесс менеджера, если он ещё не был запущен.11531154#### `class multiprocessing.managers.SyncManager`11551156Подкласс [`BaseManager`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.11571158Он также поддерживает создание разделяемых списков и словарей.11591160#### `Barrier(parties[, action[, timeout]])`11611162Создать общий объект [`threading.Barrier`](https://python-all.ru/3.5/library/threading.html#threading.Barrier) и вернуть для него прокси.11631164Новое в версии 3.3.11651166#### `BoundedSemaphore([value])`11671168Создать общий объект [`threading.BoundedSemaphore`](https://python-all.ru/3.5/library/threading.html#threading.BoundedSemaphore) и вернуть для него прокси.11691170#### `Condition([lock])`11711172Создать общий объект [`threading.Condition`](https://python-all.ru/3.5/library/threading.html#threading.Condition) и вернуть прокси для него.11731174Если *блокировка* предоставлена, то она должна быть прокси для объекта [`threading.Lock`](https://python-all.ru/3.5/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/3.5/library/threading.html#threading.RLock).11751176Изменено в версии 3.3: Был добавлен метод [`wait_for()`](https://python-all.ru/3.5/library/threading.html#threading.Condition.wait_for).11771178#### `Event()`11791180Создать общий объект [`threading.Event`](https://python-all.ru/3.5/library/threading.html#threading.Event) и вернуть прокси для него.11811182#### `Lock()`11831184Создать общий объект [`threading.Lock`](https://python-all.ru/3.5/library/threading.html#threading.Lock) и вернуть прокси для него.11851186#### `Namespace()`11871188Создать общий объект [`Namespace`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.Namespace) и вернуть прокси для него.11891190#### `Queue([maxsize])`11911192Создать общий объект [`queue.Queue`](https://python-all.ru/3.5/library/queue.html#queue.Queue) и вернуть прокси для него.11931194#### `RLock()`11951196Создать общий объект [`threading.RLock`](https://python-all.ru/3.5/library/threading.html#threading.RLock) и вернуть прокси для него.11971198#### `Semaphore([value])`11991200Создать общий объект [`threading.Semaphore`](https://python-all.ru/3.5/library/threading.html#threading.Semaphore) и вернуть прокси для него.12011202#### `Array(typecode, sequence)`12031204Создать массив и вернуть для него прокси.12051206#### `Value(typecode, value)`12071208Создать объект с атрибутом `value`, доступным для записи, и вернуть прокси для него.12091210#### `dict()`12111212#### `dict(mapping)`12131214#### `dict(sequence)`12151216Создать общий объект `dict` и вернуть прокси для него.12171218#### `list()`12191220#### `list(sequence)`12211222Создать общий объект `list` и вернуть прокси для него.12231224> **Примечание**1225>1226> Изменения изменяемых значений или элементов в прокси-объектах словарей и списков не будут распространяться через менеджер, поскольку прокси не может отследить, когда его значения или элементы изменяются. Чтобы изменить такой элемент, можно переназначить изменённый объект обратно в прокси-контейнер:1227>1228> ```python1229> # создать прокси для списка и добавить изменяемый объект (словарь)1230> lproxy = manager.list()1231> lproxy.append({})1232> # теперь изменить словарь1233> d = lproxy[0]1234> d['a'] = 11235> d['b'] = 21236> # на данный момент изменения в d еще не синхронизированы, но при1237> # при переприсваивании словаря прокси получает уведомление об изменении1238> lproxy[0] = d1239> ```12401241#### `class multiprocessing.managers.Namespace`12421243Тип, который можно зарегистрировать с помощью [`SyncManager`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.SyncManager).12441245Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.12461247Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:12481249```pycon1250>>> manager = multiprocessing.Manager()1251>>> Global = manager.Namespace()1252>>> Global.x = 101253>>> Global.y = 'hello'1254>>> Global._z = 12.3 # это атрибут прокси1255>>> print(Global)1256Namespace(x=10, y='hello')1257```12581259#### 17.2.2.7.1. Настраиваемые менеджеры12601261Чтобы создать собственный менеджер, создаётся подкласс [`BaseManager`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager) и используется метод класса [`register()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:12621263```python1264from multiprocessing.managers import BaseManager12651266class MathsClass:1267 def add(self, x, y):1268 return x + y1269 def mul(self, x, y):1270 return x * y12711272class MyManager(BaseManager):1273 pass12741275MyManager.register('Maths', MathsClass)12761277if __name__ == '__main__':1278 with MyManager() as manager:1279 maths = manager.Maths()1280 print(maths.add(4, 3)) # выводит 71281 print(maths.mul(7, 8)) # выводит 561282```12831284#### 17.2.2.7.2. Использование удалённого менеджера12851286Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).12871288Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:12891290```python1291>>> from multiprocessing.managers import BaseManager1292>>> import queue1293>>> queue = queue.Queue()1294>>> class QueueManager(BaseManager): pass1295>>> QueueManager.register('get_queue', callable=lambda:queue)1296>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1297>>> s = m.get_server()1298>>> s.serve_forever()1299```13001301Один клиент может получить доступ к серверу следующим образом:13021303```python1304>>> from multiprocessing.managers import BaseManager1305>>> class QueueManager(BaseManager): pass1306>>> QueueManager.register('get_queue')1307>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1308>>> m.connect()1309>>> queue = m.get_queue()1310>>> queue.put('hello')1311```13121313Другой клиент также может использовать его:13141315```python1316>>> from multiprocessing.managers import BaseManager1317>>> class QueueManager(BaseManager): pass1318>>> QueueManager.register('get_queue')1319>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1320>>> m.connect()1321>>> queue = m.get_queue()1322>>> queue.get()1323'hello'1324```13251326Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:13271328```python1329>>> from multiprocessing import Process, Queue1330>>> from multiprocessing.managers import BaseManager1331>>> class Worker(Process):1332... def __init__(self, q):1333... self.q = q1334... super(Worker, self).__init__()1335... def run(self):1336... self.q.put('local hello')1337...1338>>> queue = Queue()1339>>> w = Worker(queue)1340>>> w.start()1341>>> class QueueManager(BaseManager): pass1342...1343>>> QueueManager.register('get_queue', callable=lambda: queue)1344>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1345>>> s = m.get_server()1346>>> s.serve_forever()1347```13481349### 17.2.2.8. Прокси-объекты13501351Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.13521353Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:13541355```pycon1356>>> from multiprocessing import Manager1357>>> manager = Manager()1358>>> l = manager.list([i*i for i in range(10)])1359>>> print(l)1360[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1361>>> print(repr(l))1362<ListProxy object, typeid 'list' at 0x...>1363>>> l[4]1364161365>>> l[2:5]1366[4, 9, 16]1367```13681369Обратите внимание, что применение [`str()`](https://python-all.ru/3.5/library/stdtypes.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/3.5/library/functions.html#repr) вернёт представление прокси.13701371Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:13721373```pycon1374>>> a = manager.list()1375>>> b = manager.list()1376>>> a.append(b) # референт a теперь содержит референт b1377>>> print(a, b)1378[[]] []1379>>> b.append('hello')1380>>> print(a, b)1381[['hello']] ['hello']1382```13831384> **Примечание**1385>1386> Типы прокси в [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) не поддерживают сравнение по значению. Так, например:1387>1388> ```pycon1389> >>> manager.list([1,2,3]) == [1,2,3]1390> False1391> ```1392>1393> При сравнении следует просто использовать копию референта.13941395#### `class multiprocessing.managers.BaseProxy`13961397Прокси-объекты являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseProxy).13981399#### `_callmethod(methodname[, args[, kwds]])`14001401Вызывает метод референта прокси и возвращает результат.14021403Если `proxy` – это прокси, чей референт – `obj`, то выражение14041405```python1406proxy._callmethod(methodname, args, kwds)1407```14081409вычислит выражение14101411```python1412getattr(obj, methodname)(*args, **kwds)1413```14141415в процессе менеджера.14161417Возвращаемое значение будет копией результата вызова или прокси для нового разделяемого объекта – см. документацию по аргументу *method\_to\_typeid* функции [`BaseManager.register()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).14181419Если при вызове возникло исключение, то оно повторно возбуждается с помощью [`_callmethod()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). Если в процессе менеджера возникло другое исключение, то оно преобразуется в исключение `RemoteError` и возбуждается с помощью [`_callmethod()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod).14201421Обратите внимание, что исключение будет возбуждаться, если *methodname* не был *экспонирован*.14221423Пример использования [`_callmethod()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):14241425```pycon1426>>> l = manager.list(range(10))1427>>> l._callmethod('__len__')1428101429>>> l._callmethod('__getitem__', (slice(2, 7),)) # эквивалентно l[2:7]1430[2, 3, 4, 5, 6]1431>>> l._callmethod('__getitem__', (20,)) # эквивалентно l[20]1432Traceback (most recent call last):1433...1434IndexError: list index out of range1435```14361437#### `_getvalue()`14381439Возвращает копию референта.14401441Если референт не может быть сериализован (unpicklable), то это вызовет исключение.14421443#### `__repr__()`14441445Возвращает представление прокси-объекта.14461447#### `__str__()`14481449Возвращает представление референта.14501451#### 17.2.2.8.1. Очистка14521453Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.14541455Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.14561457### 17.2.2.9. Пулы процессов14581459Можно создать пул процессов, который будет выполнять задачи, отправленные ему с помощью класса [`Pool`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool).14601461#### `class multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])`14621463Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.14641465*processes* – количество используемых рабочих процессов. Если *processes* равно `None`, то используется число, возвращаемое [`os.cpu_count()`](https://python-all.ru/3.5/library/os.html#os.cpu_count).14661467Если *initializer* не равно `None`, то каждый рабочий процесс вызовет `initializer(*initargs)` при запуске.14681469*maxtasksperchild* – количество задач, которое рабочий процесс может выполнить до завершения и замены новым рабочим процессом, чтобы освободить неиспользуемые ресурсы. Значение по умолчанию *maxtasksperchild* равно `None`, что означает, что рабочие процессы будут жить столько же, сколько и пул.14701471*context* используется для указания контекста, в котором запускаются рабочие процессы. Обычно пул создаётся с помощью функции `multiprocessing.Pool()` или метода [`Pool()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool) объекта контекста. В обоих случаях *context* устанавливается соответствующим образом.14721473Обратите внимание, что методы объекта пула должны вызываться только тем процессом, который создал пул.14741475Новое в версии 3.2: *maxtasksperchild*14761477Новое в версии 3.4: *context*14781479> **Примечание**1480>1481> Рабочие процессы внутри [`Pool`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool) обычно существуют в течение всего времени работы очереди пула. Часто используемый в других системах (например, Apache, mod\_wsgi и т.д.) подход для освобождения ресурсов, удерживаемых рабочими процессами, заключается в том, чтобы позволить рабочему процессу в пуле выполнить только определённый объём работы, после чего он завершается, очищается, и на его место запускается новый процесс. Аргумент *maxtasksperchild* функции [`Pool`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool) предоставляет эту возможность конечному пользователю.14821483#### `apply(func[, args[, kwds]])`14841485Вызывает *func* с аргументами *args* и именованными аргументами *kwds*. Блокируется до получения результата. Поскольку этот метод блокируется, [`apply_async()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.apply_async) лучше подходит для параллельного выполнения работы. Кроме того, *func* выполняется только в одном из рабочих процессов пула.14861487#### `apply_async(func[, args[, kwds[, callback[, error_callback]]]])`14881489Вариант метода [`apply()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.apply), возвращающий объект результата.14901491Если указан *колбэк*, то это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк*, если только вызов не завершился ошибкой – в этом случае вместо него применяется *колбэк ошибки*.14921493Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.14941495Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.14961497#### `map(func, iterable[, chunksize])`14981499Параллельный аналог встроенной функции [`map()`](https://python-all.ru/3.5/library/functions.html#map) (однако поддерживает только один аргумент *iterable*). Блокирует выполнение до готовности результата.15001501Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.15021503#### `map_async(func, iterable[, chunksize[, callback[, error_callback]]])`15041505Вариант метода [`map()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.map), возвращающий объект результата.15061507Если указан *колбэк*, то это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк*, если только вызов не завершился ошибкой – в этом случае вместо него применяется *колбэк ошибки*.15081509Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.15101511Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.15121513#### `imap(func, iterable[, chunksize])`15141515Более ленивая версия [`map()`](https://python-all.ru/3.5/library/functions.html#map).15161517Аргумент *chunksize* совпадает с тем, что используется в методе [`map()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.map). Для очень длинных итерируемых объектов использование большого значения *chunksize* может привести к завершению задачи **значительно** быстрее, чем при использовании значения по умолчанию `1`.15181519Кроме того, если *chunksize* равно `1`, то метод `next()` итератора, возвращаемого методом [`imap()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` возбудит [`multiprocessing.TimeoutError`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.TimeoutError), если результат не может быть возвращён в течение *timeout* секунд.15201521#### `imap_unordered(func, iterable[, chunksize])`15221523То же, что и [`imap()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.imap), за исключением того, что порядок результатов из возвращаемого итератора считается произвольным. (Только когда есть только один рабочий процесс, порядок гарантированно будет «правильным».)15241525#### `starmap(func, iterable[, chunksize])`15261527Как [`map()`](https://python-all.ru/3.5/library/functions.html#map), за исключением того, что элементы *iterable* должны быть итерабельными объектами, которые распаковываются как аргументы.15281529Таким образом, *iterable* из `[(1,2), (3, 4)]` приводит к `[func(1,2), func(3,4)]`.15301531Новое в версии 3.3.15321533#### `starmap_async(func, iterable[, chunksize[, callback[, error_back]]])`15341535Комбинация [`starmap()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.starmap) и [`map_async()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.map_async), которая перебирает *iterable* из итерируемых объектов и вызывает *func* с распакованными итерируемыми объектами. Возвращает объект результата.15361537Новое в версии 3.3.15381539#### `close()`15401541Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.15421543#### `terminate()`15441545Немедленно останавливает рабочие процессы, не дожидаясь завершения незаконченных задач. Когда объект пула будет собран сборщиком мусора, [`terminate()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.terminate) будет вызван немедленно.15461547#### `join()`15481549Ожидает завершения рабочих процессов. Перед использованием [`join()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.join) необходимо вызвать [`close()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.close) или [`terminate()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.terminate).15501551Новое в версии 3.3: Объекты Pool теперь поддерживают протокол управления контекстом – см.\\n[Context Manager Types](https://python-all.ru/3.5/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__enter__) возвращает объект pool, а [`__exit__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__exit__) вызывает [`terminate()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.terminate).15521553#### `class multiprocessing.pool.AsyncResult`15541555Класс результата, возвращаемого [`Pool.apply_async()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.apply_async) и [`Pool.map_async()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool.map_async).15561557#### `get([timeout])`15581559Возвращает результат, когда он будет получен. Если *timeout* не равен `None` и результат не поступает в течение *timeout* секунд, то вызывается [`multiprocessing.TimeoutError`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.TimeoutError). Если удалённый вызов вызвал исключение, то это исключение будет повторно возбуждено [`get()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get).15601561#### `wait([timeout])`15621563Ожидает, пока результат не станет доступен или пока не пройдёт *timeout* секунд.15641565#### `ready()`15661567Возвращает, завершён ли вызов.15681569#### `successful()`15701571Возвращает, завершился ли вызов без возникновения исключения. Возбуждает [`AssertionError`](https://python-all.ru/3.5/library/exceptions.html#AssertionError), если результат ещё не готов.15721573Следующий пример демонстрирует использование пула:15741575```python1576from multiprocessing import Pool1577import time15781579def f(x):1580 return x*x15811582if __name__ == '__main__':1583 with Pool(processes=4) as pool: # запустить 4 рабочих процесса1584 result = pool.apply_async(f, (10,)) # вычислить "f(10)" асинхронно в одном процессе1585 print(result.get(timeout=1)) # выводит "100", если компьютер не *очень* медленный15861587 print(pool.map(f, range(10))) # выводит "[0, 1, 4,..., 81]"15881589 it = pool.imap(f, range(10))1590 print(next(it)) # выводит "0"1591 print(next(it)) # выводит "1"1592 print(it.next(timeout=1)) # выводит "4", если компьютер не *очень* медленный15931594 result = pool.apply_async(time.sleep, (10,))1595 print(result.get(timeout=1)) # вызывает multiprocessing.TimeoutError1596```15971598### 17.2.2.10. Слушатели и клиенты15991600Обычно обмен сообщениями между процессами осуществляется с помощью очередей или объектов [`Connection`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection), возвращаемых [`Pipe()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Pipe).16011602Однако модуль [`multiprocessing.connection`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing.connection) обеспечивает дополнительную гибкость. По сути, он предоставляет высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows. Также поддерживается *аутентификация на основе дайджеста* с использованием модуля [`hmac`](https://python-all.ru/3.5/library/hmac.html#module-hmac) и опрос нескольких подключений одновременно.16031604#### `multiprocessing.connection.deliver_challenge(connection, authkey)`16051606Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.16071608Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.AuthenticationError).16091610#### `multiprocessing.connection.answer_challenge(connection, authkey)`16111612Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.16131614Если приветственное сообщение не получено, то возбуждается [`AuthenticationError`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.AuthenticationError).16151616#### `multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`16171618Пытается установить соединение с слушателем, использующим адрес *address*, возвращая [`Connection`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection).16191620Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [Address Formats](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-address-formats))16211622Если *authenticate* равен `True` или *authkey* является байтовой строкой, то используется дайджест-аутентификация. Ключ, используемый для аутентификации, будет либо *authkey*, либо `current_process().authkey`, если *authkey* равен `None`. Если аутентификация не удалась, то возникает [`AuthenticationError`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.AuthenticationError). См. [Ключи аутентификации](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-auth-keys).16231624#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`16251626Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.16271628*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.16291630> **Примечание**1631>1632> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.16331634*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них гарантированно доступна только первая. Если *family* равно `None`, то семейство определяется по формату *address*. Если *address* также равно `None`, выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [Форматы адресов](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равно `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/3.5/library/tempfile.html#tempfile.mkstemp).16351636Если объект listener использует сокет, то *backlog* (по умолчанию 1) передаётся методу [`listen()`](https://python-all.ru/3.5/library/socket.html#socket.socket.listen) сокета после его привязки.16371638Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.16391640Если *authkey* является байтовой строкой, то она будет использоваться в качестве ключа аутентификации; в противном случае он должен быть `None`.16411642Если *authkey* равен `None` и *authenticate* равен `True`, то `current_process().authkey` используется в качестве ключа аутентификации. Если *authkey* равен `None` и *authenticate* равен `False`, то аутентификация не выполняется. Если аутентификация не удалась, то возникает [`AuthenticationError`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.AuthenticationError). См. [Ключи аутентификации](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing-auth-keys).16431644#### `accept()`16451646Принимает соединение на привязанном сокете или именованном канале объекта-слушателя и возвращает объект [`Connection`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection). Если аутентификация была предпринята и не удалась, то возникает [`AuthenticationError`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.AuthenticationError).16471648#### `close()`16491650Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.16511652Объекты Listener имеют следующие свойства только для чтения:16531654#### `address`16551656Адрес, используемый объектом Listener.16571658#### `last_accepted`16591660Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то равен `None`.16611662Новое в версии 3.3: Объекты Listener теперь поддерживают протокол управления контекстом – см.\\n[Context Manager Types](https://python-all.ru/3.5/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__enter__) возвращает объект listener, а [`__exit__()`](https://python-all.ru/3.5/library/stdtypes.html#contextmanager.__exit__) вызывает [`close()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.connection.Listener.close).16631664#### `multiprocessing.connection.wait(object_list, timeout=None)`16651666Ожидает, пока объект из *object\_list* не будет готов. Возвращает список тех объектов из *object\_list*, которые готовы. Если *timeout* – число с плавающей точкой, вызов блокируется не более чем на указанное количество секунд. Если *timeout* равен `None`, блокировка будет неограниченной. Отрицательный тайм-аут эквивалентен нулевому.16671668Как в Unix, так и в Windows объект может появиться в *object\_list*, если он16691670- читаемый объект [`Connection`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection);1671- подключенный и читаемый объект [`socket.socket`](https://python-all.ru/3.5/library/socket.html#socket.socket); или1672- атрибут [`sentinel`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.sentinel) объекта [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process).16731674Объект соединения или сокета готов, когда из него доступны для чтения данные или другой конец был закрыт.16751676**Unix**: `wait(object_list, timeout)` почти эквивалентен `select.select(object_list, [], [], timeout)`. Разница в том, что если [`select.select()`](https://python-all.ru/3.5/library/select.html#select.select) прерван сигналом, он может возбудить [`OSError`](https://python-all.ru/3.5/library/exceptions.html#OSError) с кодом ошибки `EINTR`, тогда как [`wait()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.connection.wait) этого не сделает.16771678**Windows**: Элемент в *object\_list* должен быть либо целочисленным дескриптором, который ожидаем (согласно определению из документации функции Win32 `WaitForMultipleObjects()`), либо объектом с методом `fileno()`, возвращающим дескриптор сокета или канала. (Обратите внимание: дескрипторы каналов и сокетов **не** являются ожидаемыми дескрипторами.)16791680Новое в версии 3.3.16811682**Примеры**16831684Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:16851686```python1687from multiprocessing.connection import Listener1688from array import array16891690address = ('localhost', 6000) # семейство определяется как 'AF_INET'16911692with Listener(address, authkey=b'secret password') as listener:1693 with listener.accept() as conn:1694 print('connection accepted from', listener.last_accepted)16951696 conn.send([2.25, None, 'junk', float])16971698 conn.send_bytes(b'hello')16991700 conn.send_bytes(array('i', [42, 1729]))1701```17021703Следующий код подключается к серверу и получает от него некоторые данные:17041705```python1706from multiprocessing.connection import Client1707from array import array17081709address = ('localhost', 6000)17101711with Client(address, authkey=b'secret password') as conn:1712 print(conn.recv()) # => [2.25, None, 'junk', float]17131714 print(conn.recv_bytes()) # => 'hello'17151716 arr = array('i', [0, 0, 0, 0, 0])1717 print(conn.recv_bytes_into(arr)) # => 81718 print(arr) # => array('i', [42, 1729, 0, 0, 0])1719```17201721Следующий код использует [`wait()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.connection.wait) для ожидания сообщений от нескольких процессов одновременно:17221723```python1724import time, random1725from multiprocessing import Process, Pipe, current_process1726from multiprocessing.connection import wait17271728def foo(w):1729 for i in range(10):1730 w.send((i, current_process().name))1731 w.close()17321733if __name__ == '__main__':1734 readers = []17351736 for i in range(4):1737 r, w = Pipe(duplex=False)1738 readers.append(r)1739 p = Process(target=foo, args=(w,))1740 p.start()1741 # Закрываем записывающий конец канала, чтобы убедиться, что1742 # p – единственный процесс, владеющий дескриптором канала. Это1743 # гарантирует, что когда p закроет свой дескриптор для записывающего конца,1744 # wait() немедленно сообщит, что читающий конец готов1745 w.close()17461747 while readers:1748 for r in wait(readers):1749 try:1750 msg = r.recv()1751 except EOFError:1752 readers.remove(r)1753 else:1754 print(msg)1755```17561757#### 17.2.2.10.1. Форматы адресов17581759- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.1760- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.1761- **Адрес `'AF_PIPE'` – это строка вида**17621763 `r'\\.\pipe{PipeName}'`17641765 . Чтобы использовать17661767 [`Client()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.connection.Client)17681769 для подключения к именованному каналу на удалённом компьютере17701771 *ServerName*17721773 , следует использовать адрес вида17741775 `r'\ServerName\pipe{PipeName}'`17761777 .17781779Обратите внимание: любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не `'AF_UNIX'`.17801781### 17.2.2.11. Ключи аутентификации17821783При использовании [`Connection.recv`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Connection.recv), полученные данные автоматически десериализуются. К сожалению, десериализация данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/3.5/library/hmac.html#module-hmac) для обеспечения дайджест-аутентификации.17841785Ключ аутентификации – это байтовая строка, которую можно рассматривать как пароль: после установления соединения обе стороны требуют доказательства, что другая сторона знает ключ аутентификации. (Демонстрация того, что обе стороны используют один и тот же ключ, **не** предполагает отправку ключа по соединению.)17861787Если запрошена аутентификация, но ключ аутентификации не указан, то используется возвращаемое значение `current_process().authkey` (см. [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process)). Это значение автоматически наследуется любым объектом [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process), который создаёт текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы используют один ключ аутентификации, который можно применять для настройки соединений между ними.17881789Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/3.5/library/os.html#os.urandom).17901791### 17.2.2.12. Логирование17921793Доступна поддержка журналирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/3.5/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому сообщения от разных процессов могут перемешиваться (в зависимости от типа обработчика).17941795#### `multiprocessing.get_logger()`17961797Возвращает логгер, используемый модулем [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing). При необходимости создаётся новый.17981799При первом создании логгер имеет уровень `logging.NOTSET` и не имеет обработчика по умолчанию. Сообщения, отправленные этому логгеру, по умолчанию не распространяются на корневой логгер.18001801Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.18021803#### `multiprocessing.log_to_stderr()`18041805Эта функция вызывает [`get_logger()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.get_logger), но помимо возврата регистратора, созданного get\_logger, добавляет обработчик, который отправляет вывод на [`sys.stderr`](https://python-all.ru/3.5/library/sys.html#sys.stderr), используя формат `'[%(levelname)s/%(processName)s] %(message)s'`.18061807Ниже приведён пример сеанса с включённым журналированием:18081809```python1810>>> import multiprocessing, logging1811>>> logger = multiprocessing.log_to_stderr()1812>>> logger.setLevel(logging.INFO)1813>>> logger.warning('doomed')1814[WARNING/MainProcess] doomed1815>>> m = multiprocessing.Manager()1816[INFO/SyncManager-...] child process calling self.run()1817[INFO/SyncManager-...] created temp directory /.../pymp-...1818[INFO/SyncManager-...] manager serving at '/.../listener-...'1819>>> del m1820[INFO/MainProcess] sending shutdown message to manager1821[INFO/SyncManager-...] manager exiting with exitcode 01822```18231824Полную таблицу уровней журналирования см. в модуле [`logging`](https://python-all.ru/3.5/library/logging.html#module-logging).18251826### 17.2.2.13. Модуль [`multiprocessing.dummy`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing.dummy)18271828[`multiprocessing.dummy`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing.dummy) воспроизводит API модуля [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing), но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/3.5/library/threading.html#module-threading).18291830## 17.2.3. Рекомендации по программированию18311832Существуют определённые рекомендации и идиомы, которых следует придерживаться при использовании [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing).18331834### 17.2.3.1. Все методы запуска18351836Нижеследующее относится ко всем методам запуска.18371838Избегайте разделяемого состояния18391840> По возможности следует стараться избегать передачи больших объёмов данных между процессами.1841>1842> Вероятно, лучше всего ограничиться использованием очередей или каналов для связи между процессами, а не использовать низкоуровневые примитивы синхронизации.18431844Сериализуемость18451846> Убедитесь, что аргументы методов прокси-объектов сериализуемы.18471848Потокобезопасность прокси18491850> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.1851>1852> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)18531854Присоединение процессов-зомби18551856> В Unix, когда процесс завершается, но к нему не присоединились (joined), он становится зомби. Таких процессов не должно быть много, потому что каждый раз при запуске нового процесса (или вызове1857>1858> [`active_children()`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.active_children)1859>1860> ) все завершённые процессы, к которым ещё не присоединились, будут присоединены. Кроме того, вызов1861>1862> [`Process.is_alive`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.is_alive)1863>1864> для завершённого процесса присоединяет его. Тем не менее, рекомендуется явно присоединять все запущенные процессы.18651866Лучше наследовать, чем упаковывать/распаковывать18671868> При использовании методов запуска1869>1870> *spawn*1871>1872> или1873>1874> *forkserver*1875>1876> многие типы из1877>1878> [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing)1879>1880> должны быть упаковываемыми (picklable), чтобы дочерние процессы могли их использовать. Однако обычно следует избегать отправки разделяемых объектов другим процессам через каналы или очереди. Вместо этого стоит организовать программу так, чтобы процесс, которому требуется доступ к разделяемому ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.18811882Избегайте завершения процессов18831884> Использование метода [`Process.terminate`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.terminate) для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), которые в данный момент используются процессом, станут повреждёнными или недоступными для других процессов.1885>1886> Поэтому, вероятно, лучше рассматривать использование [`Process.terminate`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.terminate) только для процессов, которые никогда не используют разделяемые ресурсы.18871888Присоединение процессов, использующих очереди18891890> Имейте в виду, что процесс, поместивший элементы в очередь, будет ждать перед завершением, пока все буферизованные элементы не будут переданы потоком «feeder» в нижележащий канал. (Дочерний процесс может вызвать метод [`Queue.cancel_join_thread`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread) очереди, чтобы избежать такого поведения.)1891>1892> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещённые в очередь, будут в конечном итоге удалены до того, как процесс будет присоединён. В противном случае нельзя быть уверенным, что процессы, поместившие элементы в очередь, завершатся. Также помните, что недемонические процессы присоединяются автоматически.1893>1894> Пример, который приведёт к взаимоблокировке:1895>1896> ```python1897> from multiprocessing import Process, Queue1898>1899> def f(q):1900> q.put('X' * 1000000)1901>1902> if __name__ == '__main__':1903> queue = Queue()1904> p = Process(target=f, args=(queue,))1905> p.start()1906> p.join() # это приводит к взаимоблокировке1907> obj = queue.get()1908> ```1909>1910> Исправление заключается в замене двух последних строк местами (или просто удалении строки с `p.join()`).19111912Явная передача ресурсов дочерним процессам19131914> В Unix при использовании метода запуска *fork* дочерний процесс может использовать общий ресурс, созданный в родительском процессе с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.1915>1916> Помимо того, что это делает код (потенциально) совместимым с Windows и другими методами запуска, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если некоторый ресурс освобождается при сборке мусора объекта в родительском процессе.1917>1918> Так, например1919>1920> ```python1921> from multiprocessing import Process, Lock1922>1923> def f():1924> ... do something using "lock" ...1925>1926> if __name__ == '__main__':1927> lock = Lock()1928> for i in range(10):1929> Process(target=f).start()1930> ```1931>1932> следует переписать как1933>1934> ```python1935> from multiprocessing import Process, Lock1936>1937> def f(l):1938> ... do something using "l" ...1939>1940> if __name__ == '__main__':1941> lock = Lock()1942> for i in range(10):1943> Process(target=f, args=(lock,)).start()1944> ```19451946Остерегайтесь замены [`sys.stdin`](https://python-all.ru/3.5/library/sys.html#sys.stdin) на «файлоподобный объект»19471948> [`multiprocessing`](https://python-all.ru/3.5/library/multiprocessing.html#module-multiprocessing) изначально безусловно вызывал:1949>1950> ```python1951> os.close(sys.stdin.fileno())1952> ```1953>1954> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:1955>1956> ```python1957> sys.stdin.close()1958> sys.stdin = open(os.open(os.devnull, os.O_RDONLY), closefd=False)1959> ```1960>1961> Это решает основную проблему столкновения процессов друг с другом, приводящую к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, которые заменяют [`sys.stdin()`](https://python-all.ru/3.5/library/sys.html#sys.stdin) на «файлоподобный объект» с буферизацией вывода. Опасность в том, что если несколько процессов вызовут [`close()`](https://python-all.ru/3.5/library/io.html#io.IOBase.close) для этого файлоподобного объекта, одни и те же данные могут быть сброшены в объект несколько раз, что приведёт к повреждению.1962>1963> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:1964>1965> ```python1966> @property1967> def cache(self):1968> pid = os.getpid()1969> if pid != self._pid:1970> self._pid = pid1971> self._cache = []1972> return self._cache1973> ```1974>1975> Для получения дополнительной информации см. [bpo-5155](https://python-all.ru/3.5/library/multiprocessing.html), [bpo-5313](https://python-all.ru/3.5/library/multiprocessing.html) и [bpo-5331](https://python-all.ru/3.5/library/multiprocessing.html)19761977### 17.2.3.2. Методы запуска *spawn* и *forkserver*19781979Есть несколько дополнительных ограничений, которые не применяются к *fork* методу запуска.19801981Упаковываемость19821983> Убедитесь, что все аргументы1984>1985> `Process.__init__()`1986>1987> можно сериализовать с помощью pickle. Кроме того, если вы создаёте подкласс1988>1989> [`Process`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process)1990>1991> , убедитесь, что экземпляры будут сериализуемы при вызове метода1992>1993> [`Process.start`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.start)1994>1995> .19961997Глобальные переменные19981999> Имейте в виду, что если код, выполняемый в дочернем процессе, попытается обратиться к глобальной переменной, то значение, которое он увидит (если оно есть), может не совпадать со значением в родительском процессе на момент вызова [`Process.start`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.Process.start).2000>2001> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.20022003Безопасный импорт главного модуля20042005> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).2006>2007> Например, при использовании методов запуска *spawn* или *forkserver* выполнение следующего модуля приведёт к ошибке [`RuntimeError`](https://python-all.ru/3.5/library/exceptions.html#RuntimeError):2008>2009> ```python2010> from multiprocessing import Process2011>2012> def foo():2013> print('hello')2014>2015> p = Process(target=foo)2016> p.start()2017> ```2018>2019> Вместо этого следует защитить «точку входа» программы с помощью `if __name__ == '__main__':` следующим образом:2020>2021> ```python2022> from multiprocessing import Process, freeze_support, set_start_method2023>2024> def foo():2025> print('hello')2026>2027> if __name__ == '__main__':2028> freeze_support()2029> set_start_method('spawn')2030> p = Process(target=foo)2031> p.start()2032> ```2033>2034> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не как замороженное приложение.)2035>2036> Это позволяет только что порождённому интерпретатору Python безопасно импортировать модуль и затем выполнить функцию `foo()` этого модуля.2037>2038> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.20392040## 17.2.4. Примеры20412042Демонстрация создания и использования настраиваемых менеджеров и прокси:20432044```python2045from multiprocessing import freeze_support2046from multiprocessing.managers import BaseManager, BaseProxy2047import operator20482049##20502051class Foo:2052 def f(self):2053 print('you called Foo.f()')2054 def g(self):2055 print('you called Foo.g()')2056 def _h(self):2057 print('you called Foo._h()')20582059# Простая функция-генератор2060def baz():2061 for i in range(10):2062 yield i*i20632064# Тип прокси для объектов-генераторов2065class GeneratorProxy(BaseProxy):2066 _exposed_ = ['__next__']2067 def __iter__(self):2068 return self2069 def __next__(self):2070 return self._callmethod('__next__')20712072# Функция для возврата модуля operator2073def get_operator_module():2074 return operator20752076##20772078class MyManager(BaseManager):2079 pass20802081# зарегистрировать класс Foo; сделать `f()` и `g()` доступными через прокси2082MyManager.register('Foo1', Foo)20832084# зарегистрировать класс Foo; сделать `g()` и `_h()` доступными через прокси2085MyManager.register('Foo2', Foo, exposed=('g', '_h'))20862087# зарегистрировать функцию-генератор baz; использовать `GeneratorProxy` для создания прокси2088MyManager.register('baz', baz, proxytype=GeneratorProxy)20892090# зарегистрировать get_operator_module(); сделать публичные функции доступными через прокси2091MyManager.register('operator', get_operator_module)20922093##20942095def test():2096 manager = MyManager()2097 manager.start()20982099 print('-' * 20)21002101 f1 = manager.Foo1()2102 f1.f()2103 f1.g()2104 assert not hasattr(f1, '_h')2105 assert sorted(f1._exposed_) == sorted(['f', 'g'])21062107 print('-' * 20)21082109 f2 = manager.Foo2()2110 f2.g()2111 f2._h()2112 assert not hasattr(f2, 'f')2113 assert sorted(f2._exposed_) == sorted(['g', '_h'])21142115 print('-' * 20)21162117 it = manager.baz()2118 for i in it:2119 print('<%d>' % i, end=' ')2120 print()21212122 print('-' * 20)21232124 op = manager.operator()2125 print('op.add(23, 45) =', op.add(23, 45))2126 print('op.pow(2, 94) =', op.pow(2, 94))2127 print('op._exposed_ =', op._exposed_)21282129##21302131if __name__ == '__main__':2132 freeze_support()2133 test()2134```21352136Использование [`Pool`](https://python-all.ru/3.5/library/multiprocessing.html#multiprocessing.pool.Pool):21372138```python2139import multiprocessing2140import time2141import random2142import sys21432144#2145# Функции, используемые тестовым кодом2146#21472148def calculate(func, args):2149 result = func(*args)2150 return '%s says that %s%s = %s' % (2151 multiprocessing.current_process().name,2152 func.__name__, args, result2153 )21542155def calculatestar(args):2156 return calculate(*args)21572158def mul(a, b):2159 time.sleep(0.5 * random.random())2160 return a * b21612162def plus(a, b):2163 time.sleep(0.5 * random.random())2164 return a + b21652166def f(x):2167 return 1.0 / (x - 5.0)21682169def pow3(x):2170 return x ** 321712172def noop(x):2173 pass21742175#2176# Тестовый код2177#21782179def test():2180 PROCESSES = 42181 print('Creating pool with %d processes\n' % PROCESSES)21822183 with multiprocessing.Pool(PROCESSES) as pool:2184 #2185 # Тесты2186 #21872188 TASKS = [(mul, (i, 7)) for i in range(10)] + \2189 [(plus, (i, 8)) for i in range(10)]21902191 results = [pool.apply_async(calculate, t) for t in TASKS]2192 imap_it = pool.imap(calculatestar, TASKS)2193 imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)21942195 print('Ordered results using pool.apply_async():')2196 for r in results:2197 print('\t', r.get())2198 print()21992200 print('Ordered results using pool.imap():')2201 for x in imap_it:2202 print('\t', x)2203 print()22042205 print('Unordered results using pool.imap_unordered():')2206 for x in imap_unordered_it:2207 print('\t', x)2208 print()22092210 print('Ordered results using pool.map() --- will block till complete:')2211 for x in pool.map(calculatestar, TASKS):2212 print('\t', x)2213 print()22142215 #2216 # Тестирование обработки ошибок2217 #22182219 print('Testing error handling:')22202221 try:2222 print(pool.apply(f, (5,)))2223 except ZeroDivisionError:2224 print('\tGot ZeroDivisionError as expected from pool.apply()')2225 else:2226 raise AssertionError('expected ZeroDivisionError')22272228 try:2229 print(pool.map(f, list(range(10))))2230 except ZeroDivisionError:2231 print('\tGot ZeroDivisionError as expected from pool.map()')2232 else:2233 raise AssertionError('expected ZeroDivisionError')22342235 try:2236 print(list(pool.imap(f, list(range(10)))))2237 except ZeroDivisionError:2238 print('\tGot ZeroDivisionError as expected from list(pool.imap())')2239 else:2240 raise AssertionError('expected ZeroDivisionError')22412242 it = pool.imap(f, list(range(10)))2243 for i in range(10):2244 try:2245 x = next(it)2246 except ZeroDivisionError:2247 if i == 5:2248 pass2249 except StopIteration:2250 break2251 else:2252 if i == 5:2253 raise AssertionError('expected ZeroDivisionError')22542255 assert i == 92256 print('\tGot ZeroDivisionError as expected from IMapIterator.next()')2257 print()22582259 #2260 # Тестирование тайм-аутов2261 #22622263 print('Testing ApplyResult.get() with timeout:', end=' ')2264 res = pool.apply_async(calculate, TASKS[0])2265 while 1:2266 sys.stdout.flush()2267 try:2268 sys.stdout.write('\n\t%s' % res.get(0.02))2269 break2270 except multiprocessing.TimeoutError:2271 sys.stdout.write('.')2272 print()2273 print()22742275 print('Testing IMapIterator.next() with timeout:', end=' ')2276 it = pool.imap(calculatestar, TASKS)2277 while 1:2278 sys.stdout.flush()2279 try:2280 sys.stdout.write('\n\t%s' % it.next(0.02))2281 except StopIteration:2282 break2283 except multiprocessing.TimeoutError:2284 sys.stdout.write('.')2285 print()2286 print()22872288if __name__ == '__main__':2289 multiprocessing.freeze_support()2290 test()2291```22922293Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:22942295```python2296import time2297import random22982299from multiprocessing import Process, Queue, current_process, freeze_support23002301#2302# Функция, выполняемая рабочими процессами2303#23042305def worker(input, output):2306 for func, args in iter(input.get, 'STOP'):2307 result = calculate(func, args)2308 output.put(result)23092310#2311# Функция, используемая для вычисления результата2312#23132314def calculate(func, args):2315 result = func(*args)2316 return '%s says that %s%s = %s' % \2317 (current_process().name, func.__name__, args, result)23182319#2320# Функции, на которые ссылаются задачи2321#23222323def mul(a, b):2324 time.sleep(0.5*random.random())2325 return a * b23262327def plus(a, b):2328 time.sleep(0.5*random.random())2329 return a + b23302331#2332#2333#23342335def test():2336 NUMBER_OF_PROCESSES = 42337 TASKS1 = [(mul, (i, 7)) for i in range(20)]2338 TASKS2 = [(plus, (i, 8)) for i in range(10)]23392340 # Создание очередей2341 task_queue = Queue()2342 done_queue = Queue()23432344 # Отправка задач2345 for task in TASKS1:2346 task_queue.put(task)23472348 # Запуск рабочих процессов2349 for i in range(NUMBER_OF_PROCESSES):2350 Process(target=worker, args=(task_queue, done_queue)).start()23512352 # Получение и вывод результатов2353 print('Unordered results:')2354 for i in range(len(TASKS1)):2355 print('\t', done_queue.get())23562357 # Добавить ещё задач с помощью `put()`2358 for task in TASKS2:2359 task_queue.put(task)23602361 # Получить и вывести ещё несколько результатов2362 for i in range(len(TASKS2)):2363 print('\t', done_queue.get())23642365 # Сообщить дочерним процессам об остановке2366 for i in range(NUMBER_OF_PROCESSES):2367 task_queue.put('STOP')23682369if __name__ == '__main__':2370 freeze_support()2371 test()2372```2373