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

multiprocessing.md

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

1> **Источник:** https://python-all.ru/3/library/multiprocessing.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `multiprocessing` – параллелизм на основе процессов89**Исходный код:** [Lib/multiprocessing/](https://python-all.ru/src/3.14/Lib/multiprocessing)1011---1213[Доступность](https://python-all.ru/3/library/intro.html#availability): не Android, не iOS, не WASI.1415Этот модуль не поддерживается на [мобильных платформах](https://python-all.ru/3/library/intro.html#mobile-availability) или [платформах WebAssembly](https://python-all.ru/3/library/intro.html#wasm-availability).1617## Введение1819`multiprocessing` – это пакет, который поддерживает порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/3/library/threading.html#module-threading). Пакет `multiprocessing` предоставляет как локальную, так и удалённую конкурентность, эффективно обходя [глобальную блокировку интерпретатора](https://python-all.ru/3/glossary.html#term-global-interpreter-lock) за счёт использования подпроцессов вместо потоков. Благодаря этому модуль `multiprocessing` позволяет программисту полностью задействовать несколько процессоров на данной машине. Он работает как на POSIX, так и Windows.2021Модуль `multiprocessing` также вводит объект [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool), который предоставляет удобный способ распараллеливания выполнения функции по множеству входных значений, распределяя входные данные между процессами (параллелизм данных). Следующий пример демонстрирует распространённую практику определения таких функций в модуле, чтобы дочерние процессы могли успешно импортировать этот модуль. Этот базовый пример параллелизма данных с использованием `Pool`,2223```python24from multiprocessing import Pool2526def f(x):27    return x*x2829if __name__ == '__main__':30    with Pool(5) as p:31        print(p.map(f, [1, 2, 3]))32```3334будет выводить на стандартный вывод3536```python37[1, 4, 9]38```3940Модуль `multiprocessing` также вводит API, которые не имеют аналогов в модуле [`threading`](https://python-all.ru/3/library/threading.html#module-threading), например возможность [`terminate`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.terminate), [`interrupt`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.interrupt) или [`kill`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.kill) запущенный процесс.4142> **См. также**43>44> [`concurrent.futures.ProcessPoolExecutor`](https://python-all.ru/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor) предлагает интерфейс более высокого уровня для отправки задач фоновому процессу без блокировки выполнения вызывающего процесса. По сравнению с прямым использованием интерфейса [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool), API [`concurrent.futures`](https://python-all.ru/3/library/concurrent.futures.html#module-concurrent.futures) позволяет легче разделить отправку работы в пул процессов и ожидание результатов.4546### Класс [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process)4748В `multiprocessing` процессы порождаются созданием объекта [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.start). `Process` следует API [`threading.Thread`](https://python-all.ru/3/library/threading.html#threading.Thread). Простой пример многопроцессной программы:4950```python51from multiprocessing import Process5253def f(name):54    print('hello', name)5556if __name__ == '__main__':57    p = Process(target=f, args=('bob',))58    p.start()59    p.join()60```6162Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:6364```python65from multiprocessing import Process66import os6768def info(title):69    print(title)70    print('module name:', __name__)71    print('parent process:', os.getppid())72    print('process id:', os.getpid())7374def f(name):75    info('function f')76    print('hello', name)7778if __name__ == '__main__':79    info('main line')80    p = Process(target=f, args=('bob',))81    p.start()82    p.join()83```8485Объяснение того, зачем нужна часть `if __name__ == '__main__'`, см. в [руководстве по программированию](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-programming).8687Аргументы [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process) обычно должны быть сериализуемыми (picklable), чтобы их можно было передать дочернему процессу. Если попытаться ввести приведённый выше пример непосредственно в REPL, это может привести к [`AttributeError`](https://python-all.ru/3/library/exceptions.html#AttributeError) в дочернем процессе при попытке найти функцию *f* в модуле `__main__`.8889### Контексты и методы запуска9091В зависимости от платформы `multiprocessing` поддерживает три способа запуска процесса. Эти *методы запуска*:9293> ***spawn***94>95> Родительский процесс запускает новый процесс интерпретатора Python. Дочерний процесс унаследует только те ресурсы, которые необходимы для выполнения метода [`run()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.run) объекта процесса. В частности, ненужные файловые дескрипторы и дескрипторы из родительского процесса не наследуются. Запуск процесса с помощью этого метода довольно медленный по сравнению с использованием *fork* или *forkserver*.96>97> Доступно на платформах POSIX и Windows. По умолчанию используется на Windows и macOS.98>99> ***fork***100>101> Родительский процесс использует [`os.fork()`](https://python-all.ru/3/library/os.html#os.fork) для форка интерпретатора Python. Дочерний процесс в начале своего выполнения фактически идентичен родительскому процессу. Все ресурсы родительского процесса наследуются дочерним процессом. Обратите внимание, что безопасный форк многопоточного процесса проблематичен.102>103> Доступно на системах POSIX.104>105> Изменено в версии 3.14: Этот метод больше не является методом запуска по умолчанию ни на одной платформе. Код, требующий *fork*, должен явно указать это через [`get_context()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.get_context) или [`set_start_method()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.set_start_method).106>107> Изменено в версии 3.12: Если Python может обнаружить, что ваш процесс имеет несколько потоков, то функция [`os.fork()`](https://python-all.ru/3/library/os.html#os.fork), вызываемая этим методом запуска внутри, вызовет исключение [`DeprecationWarning`](https://python-all.ru/3/library/exceptions.html#DeprecationWarning). Используйте другой метод запуска. Дополнительные объяснения см. в документации к `os.fork()`.108>109> ***forkserver***110>111> Когда программа запускается и выбирает метод запуска *forkserver*, порождается серверный процесс. После этого, когда требуется новый процесс, родительский процесс подключается к серверу и запрашивает создание нового процесса через fork. Серверный процесс forkserver является однопоточным, если только системные библиотеки или предварительно загруженные импорты не порождают потоки как побочный эффект, поэтому обычно он может безопасно использовать [`os.fork()`](https://python-all.ru/3/library/os.html#os.fork). Никакие ненужные ресурсы не наследуются.112>113> Доступно на платформах POSIX, поддерживающих передачу файловых дескрипторов через каналы Unix, таких как Linux. Используется по умолчанию на таких платформах.114>115> Изменено в версии 3.14: Этот метод стал методом запуска по умолчанию на платформах POSIX.116117Изменено в версии 3.4: *spawn* добавлен на всех платформах POSIX, а *forkserver* добавлен для некоторых платформ POSIX. Дочерние процессы больше не наследуют все наследуемые дескрипторы родительского процесса в Windows.118119Изменено в версии 3.8: На macOS метод запуска *spawn* теперь используется по умолчанию. Метод запуска *fork* следует считать небезопасным, так как он может приводить к сбоям подпроцесса, поскольку системные библиотеки macOS могут запускать потоки. См. [bpo-33725](https://python-all.ru/3/library/multiprocessing.html).120121Изменено в версии 3.14: На платформах POSIX метод запуска по умолчанию был изменён с *fork* на *forkserver*, чтобы сохранить производительность, но избежать типичных проблем совместимости многопоточных процессов. См. [gh-84559](https://python-all.ru/3/library/multiprocessing.html).122123На POSIX использование методов запуска *spawn* или *forkserver* также запускает процесс *resource tracker* (отслеживатель ресурсов), который отслеживает удалённые именованные системные ресурсы (такие как именованные семафоры или объекты [`SharedMemory`](https://python-all.ru/3/library/multiprocessing.shared_memory.html#multiprocessing.shared_memory.SharedMemory)), созданные процессами программы. Когда все процессы завершатся, отслеживатель ресурсов удаляет все оставшиеся отслеживаемые объекты. Обычно их не должно быть, но если процесс был убит сигналом, могут остаться некоторые «утекшие» ресурсы. (Ни утекшие семафоры, ни разделяемые сегменты памяти не будут автоматически удалены до следующей перезагрузки. Это проблематично для обоих типов объектов, так как система допускает лишь ограниченное количество именованных семафоров, а разделяемые сегменты памяти занимают некоторое место в основной памяти.)124125Для выбора метода запуска используется [`set_start_method()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.set_start_method) в блоке `if __name__ == '__main__'` главного модуля. Например:126127```python128import multiprocessing as mp129130def foo(q):131    q.put('hello')132133if __name__ == '__main__':134    mp.set_start_method('spawn')135    q = mp.Queue()136    p = mp.Process(target=foo, args=(q,))137    p.start()138    print(q.get())139    p.join()140```141142[`set_start_method()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.set_start_method) не должен использоваться более одного раза в программе.143144В качестве альтернативы можно использовать [`get_context()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.get_context) для получения объекта контекста. Объекты контекста имеют тот же API, что и модуль multiprocessing, и позволяют использовать несколько методов запуска в одной программе.145146```python147import multiprocessing as mp148149def foo(q):150    q.put('hello')151152if __name__ == '__main__':153    ctx = mp.get_context('spawn')154    q = ctx.Queue()155    p = ctx.Process(target=foo, args=(q,))156    p.start()157    print(q.get())158    p.join()159```160161Обратите внимание: объекты, связанные с одним контекстом, могут быть несовместимы с процессами другого контекста. В частности, блокировки, созданные с помощью контекста *fork*, нельзя передавать процессам, запущенным с помощью методов запуска *spawn* или *forkserver*.162163Библиотеки, использующие `multiprocessing` или [`ProcessPoolExecutor`](https://python-all.ru/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor), должны быть спроектированы так, чтобы позволять своим пользователям указывать собственный контекст multiprocessing. Использование определённого контекста внутри библиотеки может привести к несовместимости с остальной частью приложения пользователя библиотеки. Всегда документируйте, требует ли ваша библиотека конкретного метода запуска.164165> **Предупреждение**166>167> Методы запуска `'spawn'` и `'forkserver'`, как правило, нельзя использовать с «замороженными» исполняемыми файлами (т.е. бинарными файлами, созданными пакетами вроде **PyInstaller** и **cx\_Freeze**) в системах POSIX. Метод запуска `'fork'` может работать, если код не использует потоки.168169### Обмен объектами между процессами170171`multiprocessing` поддерживает два типа каналов связи между процессами:172173**Очереди**174175> Класс [`Queue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue) является почти точной копией [`queue.Queue`](https://python-all.ru/3/library/queue.html#queue.Queue). Например:176>177> ```python178> from multiprocessing import Process, Queue179>180> def f(q):181>     q.put([42, None, 'hello'])182>183> if __name__ == '__main__':184>     q = Queue()185>     p = Process(target=f, args=(q,))186>     p.start()187>     print(q.get())    # печатает "[42, None, 'hello']"188>     p.join()189> ```190>191> Очереди являются потокобезопасными и безопасными для процессов. Любой объект, помещённый в очередь `multiprocessing`, будет сериализован.192193**Каналы**194195> Функция [`Pipe()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является дуплексным (двунаправленным). Например:196>197> ```python198> from multiprocessing import Process, Pipe199>200> def f(conn):201>     conn.send([42, None, 'hello'])202>     conn.close()203>204> if __name__ == '__main__':205>     parent_conn, child_conn = Pipe()206>     p = Process(target=f, args=(child_conn,))207>     p.start()208>     print(parent_conn.recv())   # печатает "[42, None, 'hello']"209>     p.join()210> ```211>212> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы `send()` и `recv()` (среди прочих). Обратите внимание: данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.213>214> Метод `send()` сериализует объект, а `recv()` воссоздаёт объект.215216### Синхронизация между процессами217218`multiprocessing` содержит эквиваленты всех примитивов синхронизации из [`threading`](https://python-all.ru/3/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс одновременно выводит на стандартный вывод:219220```python221from multiprocessing import Process, Lock222223def f(l, i):224    l.acquire()225    try:226        print('hello world', i)227    finally:228        l.release()229230if __name__ == '__main__':231    lock = Lock()232233    for num in range(10):234        Process(target=f, args=(lock, num)).start()235```236237Без использования блокировки вывод от разных процессов может полностью перепутаться.238239### Совместное использование состояния между процессами240241Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.242243Однако если действительно необходимо использовать некоторые общие данные, то `multiprocessing` предоставляет несколько способов сделать это.244245**Разделяемая память**246247> Данные могут храниться в отображении разделяемой памяти с помощью [`Value`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Array). Например, следующий код248>249> ```python250> from multiprocessing import Process, Value, Array251>252> def f(n, a):253>     n.value = 3.1415927254>     for i in range(len(a)):255>         a[i] = -a[i]256>257> if __name__ == '__main__':258>     num = Value('d', 0.0)259>     arr = Array('i', range(10))260>261>     p = Process(target=f, args=(num, arr))262>     p.start()263>     p.join()264>265>     print(num.value)266>     print(arr[:])267> ```268>269> выведет270>271> ```python272> 3.1415927273> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]274> ```275>276> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, аналогичные используемым модулем [`array`](https://python-all.ru/3/library/array.html#module-array): `'d'` означает число с плавающей запятой двойной точности, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасными для процессов и потоков.277>278> Для большей гибкости при работе с разделяемой памятью можно использовать модуль [`multiprocessing.sharedctypes`](https://python-all.ru/3/library/multiprocessing.html#module-multiprocessing.sharedctypes), который поддерживает создание произвольных объектов ctypes, выделенных из разделяемой памяти.279280**Серверный процесс**281282> Объект-менеджер, возвращаемый [`Manager()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Manager), управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими с помощью прокси.283>284> Менеджер, возвращаемый [`Manager()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Manager), будет поддерживать типы [`list`](https://python-all.ru/3/library/stdtypes.html#list), [`dict`](https://python-all.ru/3/library/stdtypes.html#dict), [`set`](https://python-all.ru/3/library/stdtypes.html#set), [`Namespace`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.Namespace), [`Lock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Event), [`Barrier`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Barrier), [`Queue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Array). Например,285>286> ```python287> from multiprocessing import Process, Manager288>289> def f(d, l, s):290>     d[1] = '1'291>     d['2'] = 2292>     d[0.25] = None293>     l.reverse()294>     s.add('a')295>     s.add('b')296>297> if __name__ == '__main__':298>     with Manager() as manager:299>         d = manager.dict()300>         l = manager.list(range(10))301>         s = manager.set()302>303>         p = Process(target=f, args=(d, l, s))304>         p.start()305>         p.join()306>307>         print(d)308>         print(l)309>         print(s)310> ```311>312> выведет313>314> ```python315> {0.25: None, 1: '1', '2': 2}316> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]317> {'a', 'b'}318> ```319>320> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.321322### Использование пула рабочих процессов323324Класс [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool) представляет пул рабочих процессов. Он имеет методы, которые позволяют передавать задачи рабочим процессам несколькими различными способами.325326Например:327328```python329from multiprocessing import Pool, TimeoutError330import time331import os332333def f(x):334    return x*x335336if __name__ == '__main__':337    # запускает 4 рабочих процесса338    with Pool(processes=4) as pool:339340        # печатает "[0, 1, 4,..., 81]"341        print(pool.map(f, range(10)))342343        # печатает те же числа в произвольном порядке344        for i in pool.imap_unordered(f, range(10)):345            print(i)346347        # вычисляет "f(20)" асинхронно348        res = pool.apply_async(f, (20,))      # выполняется *только* в одном процессе349        print(res.get(timeout=1))             # печатает "400"350351        # вычисляет "os.getpid()" асинхронно352        res = pool.apply_async(os.getpid, ()) # выполняется *только* в одном процессе353        print(res.get(timeout=1))             # печатает PID этого процесса354355        # запуск нескольких асинхронных вычислений *может* задействовать больше процессов356        multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]357        print([res.get(timeout=1) for res in multiple_results])358359        # усыпить один рабочий процесс на 10 секунд360        res = pool.apply_async(time.sleep, (10,))361        try:362            print(res.get(timeout=1))363        except TimeoutError:364            print("We lacked patience and got a multiprocessing.TimeoutError")365366        print("For the moment, the pool remains available for more work")367368    # выход из блока 'with' остановил пул369    print("Now the pool is closed and no longer available")370```371372Обратите внимание: методы пула должны использоваться только тем процессом, который его создал.373374> **Примечание**375>376> Для функциональности этого пакета требуется, чтобы модуль `__main__` был импортируемым дочерними процессами. Это описано в [Рекомендациях по программированию](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-programming), но стоит упомянуть об этом и здесь. Это означает, что некоторые примеры, такие как примеры [`multiprocessing.pool.Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool), не будут работать в интерактивном интерпретаторе. Например:377>378> ```python379> >>> from multiprocessing import Pool380> >>> p = Pool(5)381> >>> def f(x):382> ...     return x*x383> ...384> >>> with p:385> ...     p.map(f, [1,2,3])386> Process PoolWorker-1:387> Process PoolWorker-2:388> Process PoolWorker-3:389> Traceback (most recent call last):390> Traceback (most recent call last):391> Traceback (most recent call last):392> AttributeError: Can't get attribute 'f' on <module '__main__' (<class '_frozen_importlib.BuiltinImporter'>)>393> AttributeError: Can't get attribute 'f' on <module '__main__' (<class '_frozen_importlib.BuiltinImporter'>)>394> AttributeError: Can't get attribute 'f' on <module '__main__' (<class '_frozen_importlib.BuiltinImporter'>)>395> ```396>397> (Если попробовать это сделать, на самом деле будет выведено три полных трассировки стека, перемешанных в полу-случайном порядке, и, возможно, придётся как-то остановить родительский процесс.)398399## Ссылка400401Пакет `multiprocessing` в основном повторяет API модуля [`threading`](https://python-all.ru/3/library/threading.html#module-threading).402403### Глобальный метод запуска404405Python поддерживает несколько способов создания и инициализации процесса. Глобальный метод запуска задаёт механизм по умолчанию для создания процесса.406407Некоторые функции и методы multiprocessing, которые также могут создавать определённые объекты, неявно устанавливают глобальный метод запуска в системный по умолчанию, если он ещё не был установлен. Глобальный метод запуска можно установить только один раз. Если требуется изменить метод запуска с системного по умолчанию, необходимо заранее установить глобальный метод запуска до вызова функций или методов или создания этих объектов.408409### [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process) и исключения410411#### `class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)`412413Объекты Process представляют активность, выполняемую в отдельном процессе. Класс `Process` имеет эквиваленты всех методов [`threading.Thread`](https://python-all.ru/3/library/threading.html#threading.Thread).414415Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должен быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/3/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию `None`, то есть ничего не вызывается. *name* – это имя процесса (см. [`name`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.name) для подробностей). *args* – кортеж аргументов для вызова целевой функции. *kwargs* – это словарь именованных аргументов для вызова целевой функции. Если указан, аргумент только по ключевому слову *daemon* устанавливает флаг процесса [`daemon`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.daemon) в `True` или `False`. Если `None` (по умолчанию), этот флаг будет унаследован от создающего процесса.416417По умолчанию в *target* не передаётся никаких аргументов. Аргумент *args*, по умолчанию равный `()`, можно использовать для указания списка или кортежа аргументов, передаваемых в *target*.418419Если подкласс переопределяет конструктор, он должен вызывать конструктор базового класса (`super().__init__()`) перед любыми другими действиями над процессом.420421> **Примечание**422>423> В общем случае все аргументы `Process` должны быть упаковываемы (picklable). Это часто наблюдается при попытке создать `Process` или использовать [`concurrent.futures.ProcessPoolExecutor`](https://python-all.ru/3/library/concurrent.futures.html#concurrent.futures.ProcessPoolExecutor) из REPL с локально определённой функцией *target*.424>425> Передача вызываемого объекта, определённого в текущей сессии REPL, приводит к тому, что дочерний процесс завершается из-за необработанного исключения [`AttributeError`](https://python-all.ru/3/library/exceptions.html#AttributeError) при запуске, поскольку *target* должен быть определён внутри импортируемого модуля, чтобы его можно было загрузить при распаковке (unpickling).426>427> Пример этой неперехватываемой ошибки от дочернего процесса:428>429> ```python430> >>> import multiprocessing as mp431> >>> def knigit():432> ...     print("Ni!")433> ...434> >>> process = mp.Process(target=knigit)435> >>> process.start()436> >>> Traceback (most recent call last):437>   File ".../multiprocessing/spawn.py", line ..., in spawn_main438>   File ".../multiprocessing/spawn.py", line ..., in _main439> AttributeError: module '__main__' has no attribute 'knigit'440> >>> process441> <SpawnProcess name='SpawnProcess-1' pid=379473 parent=378707 stopped exitcode=1>442> ```443>444> См. [Методы запуска spawn и forkserver](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-programming-spawn). Хотя это ограничение не действует при использовании метода запуска `"fork"`, начиная с Python `3.14` он больше не является методом по умолчанию ни на одной платформе. См. [Контексты и методы запуска](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-start-methods). См. также [gh-132898](https://python-all.ru/3/library/multiprocessing.html).445446Изменено в версии 3.3: Добавлен параметр *daemon*.447448#### `run()`449450Метод, представляющий активность процесса.451452Этот метод можно переопределить в подклассе. Стандартный метод `run()` вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.453454Использование списка или кортежа в качестве аргумента *args*, переданного в `Process`, даёт тот же эффект.455456Пример:457458```python459>>> from multiprocessing import Process460>>> p = Process(target=print, args=[1])461>>> p.run()4621463>>> p = Process(target=print, args=(1,))464>>> p.run()4651466```467468#### `start()`469470Запускает активность процесса.471472Этот метод должен быть вызван не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.473474#### `join([timeout])`475476Если необязательный аргумент *timeout* равен `None` (по умолчанию), метод блокируется до завершения процесса, у которого вызывается метод `join()`. Если *timeout* – положительное число, блокировка длится не более *timeout* секунд. Обратите внимание: метод возвращает `None`, если процесс завершился или истекло время ожидания. Проверьте [`exitcode`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.exitcode) процесса, чтобы определить, завершился ли он.477478Процесс может быть присоединён много раз.479480Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.481482#### `name`483484Имя процесса. Имя – это строка, используемая только для идентификации. Оно не имеет семантического значения. Несколько процессов могут иметь одно и то же имя.485486Начальное имя устанавливается конструктором. Если явное имя не передано конструктору, создаётся имя вида ‘Process-N1:N2:…:Nk’, где каждая Nk – это N-й дочерний процесс своего родителя.487488#### `is_alive()`489490Возвращает, жив ли процесс.491492Грубо говоря, объект процесса живёт с момента возврата метода [`start()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.493494#### `daemon`495496Флаг демона процесса (логическое значение). Должен быть установлен до вызова [`start()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.start).497498Начальное значение наследуется от создающего процесса.499500Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.501502Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.503504В дополнение к API [`threading.Thread`](https://python-all.ru/3/library/threading.html#threading.Thread) объекты `Process` также поддерживают следующие атрибуты и методы:505506#### `pid`507508Возвращает идентификатор процесса. До запуска процесса это будет `None`.509510#### `exitcode`511512Код выхода дочернего процесса. Будет равно `None`, если процесс ещё не завершён.513514Если метод [`run()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.run) дочернего процесса завершился нормально, код выхода будет 0. Если он был завершён через [`sys.exit()`](https://python-all.ru/3/library/sys.html#sys.exit) с целочисленным аргументом *N*, код выхода будет *N*.515516Если дочерний процесс завершился из-за необработанного исключения внутри [`run()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.run), код выхода будет 1. Если он был завершён сигналом *N*, код выхода будет отрицательным значением *-N*.517518#### `authkey`519520Ключ аутентификации процесса (байтовая строка).521522При инициализации `multiprocessing` главному процессу назначается случайная строка с помощью [`os.urandom()`](https://python-all.ru/3/library/os.html#os.urandom).523524При создании объекта `Process` он наследует ключ аутентификации своего родительского процесса, хотя его можно изменить, установив [`authkey`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.authkey) в другую байтовую строку.525526См. [Ключи аутентификации](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-auth-keys).527528#### `sentinel`529530Числовой дескриптор системного объекта, который становится «готов» по завершении процесса.531532Это значение можно использовать для ожидания нескольких событий одновременно с помощью [`multiprocessing.connection.wait()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.wait). В противном случае вызов [`join()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.join) проще.533534В Windows это дескриптор ОС, используемый с семейством `WaitForSingleObject` и `WaitForMultipleObjects` API-вызовов. В POSIX это файловый дескриптор, используемый с примитивами из модуля [`select`](https://python-all.ru/3/library/select.html#module-select).535536Добавлено в версии 3.3.537538#### `interrupt()`539540Прерывает процесс. В POSIX используется сигнал [`SIGINT`](https://python-all.ru/3/library/signal.html#signal.SIGINT). Поведение в Windows не определено.541542По умолчанию завершает дочерний процесс, возбуждая [`KeyboardInterrupt`](https://python-all.ru/3/library/exceptions.html#KeyboardInterrupt). Это поведение можно изменить, установив соответствующий обработчик сигнала в дочернем процессе [`signal.signal()`](https://python-all.ru/3/library/signal.html#signal.signal) для [`SIGINT`](https://python-all.ru/3/library/signal.html#signal.SIGINT).543544Примечание: если дочерний процесс перехватывает и игнорирует [`KeyboardInterrupt`](https://python-all.ru/3/library/exceptions.html#KeyboardInterrupt), процесс не будет завершён.545546Примечание: поведение по умолчанию также установит [`exitcode`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.exitcode) в `1`, как если бы необработанное исключение было возбуждено в дочернем процессе. Чтобы получить другой `exitcode`, можно просто перехватить [`KeyboardInterrupt`](https://python-all.ru/3/library/exceptions.html#KeyboardInterrupt) и вызвать `exit(your_code)`.547548Добавлено в версии 3.14.549550#### `terminate()`551552Завершает процесс. В POSIX это делается с помощью сигнала [`SIGTERM`](https://python-all.ru/3/library/signal.html#signal.SIGTERM); в Windows используется `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.п. не будут выполнены.553554Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.555556> **Предупреждение**557>558> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.559560#### `kill()`561562То же, что и [`terminate()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.terminate), но с помощью сигнала `SIGKILL` в POSIX.563564Добавлено в версии 3.7.565566#### `close()`567568Закрывает объект `Process`, освобождая все связанные с ним ресурсы. Если соответствующий процесс ещё выполняется, возбуждается [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError). После успешного возврата из `close()` большинство других методов и атрибутов объекта `Process` будут возбуждать `ValueError`.569570Добавлено в версии 3.7.571572Обратите внимание: методы [`start()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.is_alive), [`terminate()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.terminate) и [`exitcode`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.exitcode) должны вызываться только тем процессом, который создал объект процесса.573574Пример использования некоторых методов `Process`:575576```pycon577>>> import multiprocessing, time, signal578>>> mp_context = multiprocessing.get_context('spawn')579>>> p = mp_context.Process(target=time.sleep, args=(1000,))580>>> print(p, p.is_alive())581<...Process ... initial> False582>>> p.start()583>>> print(p, p.is_alive())584<...Process ... started> True585>>> p.terminate()586>>> time.sleep(0.1)587>>> print(p, p.is_alive())588<...Process ... stopped exitcode=-SIGTERM> False589>>> p.exitcode == -signal.SIGTERM590True591```592593#### `exception multiprocessing.ProcessError`594595Базовый класс всех исключений `multiprocessing`.596597#### `exception multiprocessing.BufferTooShort`598599Исключение, возбуждаемое `Connection.recv_bytes_into()`, когда предоставленный буфер слишком мал для чтения сообщения.600601Если `e` является экземпляром `BufferTooShort`, то `e.args[0]` вернёт сообщение в виде байтовой строки.602603#### `exception multiprocessing.AuthenticationError`604605Возбуждается при ошибке аутентификации.606607#### `exception multiprocessing.TimeoutError`608609Возбуждается методами с тайм-аутом по истечении тайм-аута.610611### Каналы и очереди612613При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.614615Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает несколько производителей и потребителей).616617Типы [`Queue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue), [`SimpleQueue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.SimpleQueue) и [`JoinableQueue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.JoinableQueue) – это очереди с несколькими производителями и потребителями, работающие по принципу FIFO и созданные по образцу класса [`queue.Queue`](https://python-all.ru/3/library/queue.html#queue.Queue) из стандартной библиотеки. Они отличаются тем, что в `Queue` отсутствуют методы [`task_done()`](https://python-all.ru/3/library/queue.html#queue.Queue.task_done) и [`join()`](https://python-all.ru/3/library/queue.html#queue.Queue.join), введённые в класс `queue.Queue` в Python 2.5.618619Если используется [`JoinableQueue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.JoinableQueue), то **обязательно** вызывайте [`JoinableQueue.task_done()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди; в противном случае семафор, используемый для подсчёта незавершённых задач, может переполниться, что приведёт к возбуждению исключения.620621Одно из отличий от других реализаций очередей в Python заключается в том, что очереди `multiprocessing` сериализуют все помещаемые в них объекты с помощью [`pickle`](https://python-all.ru/3/library/pickle.html#module-pickle). Объект, возвращаемый методом get, – это заново созданный объект, не разделяющий память с исходным объектом.622623Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [Менеджеры](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-managers).624625> **Примечание**626>627> `multiprocessing` использует обычные исключения [`queue.Empty`](https://python-all.ru/3/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3/library/queue.html#queue.Full) для сигнализации тайм-аута. Они недоступны в пространстве имён `multiprocessing`, поэтому их необходимо импортировать из [`queue`](https://python-all.ru/3/library/queue.html#module-queue).628629> **Примечание**630>631> Когда объект помещается в очередь, он сериализуется с помощью модуля pickle, и фоновый поток позднее сбрасывает сериализованные данные в нижележащий канал. Это имеет некоторые неожиданные последствия, но обычно не вызывает практических трудностей – если это действительно беспокоит, можно вместо этого использовать очередь, созданную с помощью [менеджера](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-managers).632>633> 1. После помещения объекта в пустую очередь может пройти бесконечно малая задержка, прежде чем метод [`empty()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.empty) очереди вернёт [`False`](https://python-all.ru/3/library/constants.html#False) и [`get_nowait()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.get_nowait) сможет вернуться без возбуждения [`queue.Empty`](https://python-all.ru/3/library/queue.html#queue.Empty).634> 2. Если несколько процессов помещают объекты в очередь, возможно получение объектов на другом конце не по порядку. Однако объекты, помещённые в очередь одним и тем же процессом, всегда будут идти друг за другом в ожидаемом порядке.635636> **Предупреждение**637>638> Если процесс завершается принудительно с помощью [`Process.terminate()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/3/library/os.html#os.kill) в то время, как он пытается использовать [`Queue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue), данные в очереди могут быть повреждены. Это может привести к тому, что любой другой процесс при попытке использовать очередь получит исключение.639640> **Предупреждение**641>642> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не использовал [`JoinableQueue.cancel_join_thread`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread)), он не завершится, пока все буферизованные элементы не будут сброшены в канал.643>644> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.645>646> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [Рекомендации по программированию](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-programming).647648Пример использования очередей для межпроцессного взаимодействия см. в разделе [Примеры](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-examples).649650#### `multiprocessing.Pipe(duplex=True)`651652Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection), представляющих концы канала.653654Если *duplex* равно `True` (по умолчанию), то канал двунаправленный. Если *duplex* равно `False`, то канал однонаправленный: `conn1` можно использовать только для получения сообщений, а `conn2` – только для отправки.655656Метод `send()` сериализует объект с помощью [`pickle`](https://python-all.ru/3/library/pickle.html#module-pickle), а `recv()` воссоздаёт объект.657658#### `class multiprocessing.Queue([maxsize])`659660Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.661662Создание экземпляра этого класса может установить глобальный метод запуска. Подробнее см. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method).663664Обычные исключения [`queue.Empty`](https://python-all.ru/3/library/queue.html#queue.Empty) и [`queue.Full`](https://python-all.ru/3/library/queue.html#queue.Full) из модуля [`queue`](https://python-all.ru/3/library/queue.html#module-queue) стандартной библиотеки возбуждаются для сигнализации тайм-аутов.665666`Queue` реализует все методы [`queue.Queue`](https://python-all.ru/3/library/queue.html#queue.Queue), за исключением [`task_done()`](https://python-all.ru/3/library/queue.html#queue.Queue.task_done), [`join()`](https://python-all.ru/3/library/queue.html#queue.Queue.join) и [`shutdown()`](https://python-all.ru/3/library/queue.html#queue.Queue.shutdown).667668#### `qsize()`669670Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.671672Обратите внимание, что это может возбудить [`NotImplementedError`](https://python-all.ru/3/library/exceptions.html#NotImplementedError) на таких платформах, как macOS, где `sem_getvalue()` не реализован.673674#### `empty()`675676Возвращает `True`, если очередь пуста, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.677678Может возбудить [`OSError`](https://python-all.ru/3/library/exceptions.html#OSError) для закрытых очередей (не гарантируется).679680#### `full()`681682Возвращает `True`, если очередь заполнена, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.683684#### `put(obj[, block[, timeout]])`685686Помещает obj в очередь. Если необязательный аргумент *block* равен `True` (по умолчанию) и *timeout* равен `None` (по умолчанию), при необходимости блокируется до появления свободного места. Если *timeout* положительное число, блокируется не более чем на *timeout* секунд и возбуждает исключение [`queue.Full`](https://python-all.ru/3/library/queue.html#queue.Full), если за это время свободное место не появилось. В противном случае (*block* равен `False`) помещает элемент в очередь, если свободное место доступно немедленно, иначе возбуждает исключение `queue.Full` (*timeout* в этом случае игнорируется).687688Изменено в версии 3.8: Если очередь закрыта, возбуждается [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError) вместо [`AssertionError`](https://python-all.ru/3/library/exceptions.html#AssertionError).689690#### `put_nowait(obj)`691692Эквивалентно `put(obj, False)`.693694#### `get([block[, timeout]])`695696Удаляет и возвращает элемент из очереди. Если необязательный аргумент *block* равен `True` (по умолчанию) и *timeout* равен `None` (по умолчанию), при необходимости блокируется до появления элемента. Если *timeout* положительное число, блокируется не более чем на *timeout* секунд и возбуждает исключение [`queue.Empty`](https://python-all.ru/3/library/queue.html#queue.Empty), если за это время элемент не появился. В противном случае (block равен `False`) возвращает элемент, если он доступен немедленно, иначе возбуждает исключение `queue.Empty` (*timeout* в этом случае игнорируется).697698Изменено в версии 3.8: Если очередь закрыта, возбуждается [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError) вместо [`OSError`](https://python-all.ru/3/library/exceptions.html#OSError).699700#### `get_nowait()`701702Эквивалентно `get(False)`.703704`multiprocessing.Queue` имеет несколько дополнительных методов, отсутствующих в [`queue.Queue`](https://python-all.ru/3/library/queue.html#queue.Queue). Обычно эти методы не нужны в большинстве кода:705706#### `close()`707708Закрывает очередь: освобождает внутренние ресурсы.709710После закрытия очередь больше нельзя использовать. Например, [`get()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.get), [`put()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.put) и [`empty()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.empty) методы больше не должны вызываться.711712Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Это вызывается автоматически при сборке мусора очереди.713714#### `join_thread()`715716Присоединяет фоновый поток. Этот метод можно использовать только после вызова [`close()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.close). Он блокируется до завершения фонового потока, гарантируя, что все данные из буфера сброшены в канал.717718По умолчанию, если процесс не является создателем очереди, при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы сделать `join_thread()` бездействующим.719720#### `cancel_join_thread()`721722Предотвращает блокировку [`join_thread()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.join_thread). В частности, это предотвращает автоматическое присоединение фонового потока при завершении процесса – см. `join_thread()`.723724Более удачным названием для этого метода могло бы быть `allow_exit_without_flush()`. Он, скорее всего, приведёт к потере поставленных в очередь данных, и вам почти наверняка не понадобится его использовать. Он существует только на случай, если нужно немедленно завершить текущий процесс, не дожидаясь сброса данных из очереди в нижележащий канал, и потеря данных не имеет значения.725726> **Примечание**727>728> Функциональность этого класса требует работающей реализации общего семафора в хост-операционной системе. Без неё функциональность класса будет отключена, а попытки создать экземпляр `Queue` приведут к [`ImportError`](https://python-all.ru/3/library/exceptions.html#ImportError). См. [bpo-3770](https://python-all.ru/3/library/multiprocessing.html) для дополнительной информации. То же самое относится и ко всем специализированным типам очередей, перечисленным ниже.729730#### `class multiprocessing.SimpleQueue`731732Это упрощённый тип [`Queue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue), очень похожий на [`Pipe`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Pipe) с блокировкой.733734Создание экземпляра этого класса может установить глобальный метод запуска. Подробнее см. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method).735736#### `close()`737738Закрыть очередь: освободить внутренние ресурсы.739740После закрытия очередь нельзя использовать. Например, методы [`get()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.SimpleQueue.get), [`put()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.SimpleQueue.put) и [`empty()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.SimpleQueue.empty) больше нельзя вызывать.741742Добавлено в версии 3.9.743744#### `empty()`745746Возвращает `True`, если очередь пуста, иначе `False`.747748Всегда вызывает [`OSError`](https://python-all.ru/3/library/exceptions.html#OSError), если SimpleQueue закрыта.749750#### `get()`751752Извлекает и возвращает элемент из очереди.753754#### `put(item)`755756Помещает *item* в очередь.757758#### `class multiprocessing.JoinableQueue([maxsize])`759760`JoinableQueue`, подкласс [`Queue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно имеет методы [`task_done()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.JoinableQueue.join).761762Создание экземпляра этого класса может установить глобальный метод запуска. Подробнее см. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method).763764#### `task_done()`765766Указывает, что ранее поставленная в очередь задача завершена. Используется потребителями очереди. Для каждого [`get()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.get), используемого для получения задачи, последующий вызов `task_done()` сообщает очереди, что обработка задачи завершена.767768Если [`join()`](https://python-all.ru/3/library/queue.html#queue.Queue.join) в данный момент заблокирован, он возобновится, когда все элементы будут обработаны (то есть для каждого элемента, который был [`put()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.put) в очередь, был получен вызов `task_done()`).769770Вызывает [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError), если вызывается больше раз, чем было помещено элементов в очередь.771772#### `join()`773774Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.775776Счётчик незавершённых задач увеличивается каждый раз, когда элемент добавляется в очередь. Счётчик уменьшается, когда потребитель вызывает [`task_done()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы указать, что элемент извлечён и вся работа над ним завершена. Когда счётчик незавершённых задач падает до нуля, [`join()`](https://python-all.ru/3/library/queue.html#queue.Queue.join) разблокируется.777778### Разное779780#### `multiprocessing.active_children()`781782Возвращает список всех активных дочерних процессов текущего процесса.783784Вызов этой функции имеет побочный эффект – «присоединение» всех уже завершённых процессов.785786#### `multiprocessing.cpu_count()`787788Возвращает количество процессоров в системе.789790Это число не эквивалентно количеству процессоров, которые может использовать текущий процесс. Количество доступных процессоров можно получить с помощью [`os.process_cpu_count()`](https://python-all.ru/3/library/os.html#os.process_cpu_count) (или `len(os.sched_getaffinity(0))`).791792Если количество процессоров не удаётся определить, вызывается [`NotImplementedError`](https://python-all.ru/3/library/exceptions.html#NotImplementedError).793794> **См. также**795>796> [`os.cpu_count()`](https://python-all.ru/3/library/os.html#os.cpu_count) [`os.process_cpu_count()`](https://python-all.ru/3/library/os.html#os.process_cpu_count)797798Changed in version 3.13: The return value can also be overridden using the [`-X cpu_count`](https://python-all.ru/3/using/cmdline.html#cmdoption-X) flag or [`PYTHON_CPU_COUNT`](https://python-all.ru/3/using/cmdline.html#envvar-PYTHON_CPU_COUNT) as this is merely a wrapper around the [`os`](https://python-all.ru/3/library/os.html#module-os) cpu count APIs.799800#### `multiprocessing.current_process()`801802Возвращает объект [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.803804Аналог [`threading.current_thread()`](https://python-all.ru/3/library/threading.html#threading.current_thread).805806#### `multiprocessing.parent_process()`807808Возвращает объект [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process), соответствующий родительскому процессу [`current_process()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.current_process). Для главного процесса `parent_process` будет `None`.809810Добавлено в версии 3.8.811812#### `multiprocessing.freeze_support()`813814Добавляет поддержку для случая, когда программа, использующая `multiprocessing`, была заморожена для создания исполняемого файла. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)815816Эту функцию необходимо вызвать сразу после строки `if __name__ == '__main__'` в главном модуле. Например:817818```python819from multiprocessing import Process, freeze_support820821def f():822    print('hello world!')823824if __name__ == '__main__':825    freeze_support()826    Process(target=f).start()827```828829Если строка `freeze_support()` опущена, то попытка запустить замороженный исполняемый файл вызовет [`RuntimeError`](https://python-all.ru/3/library/exceptions.html#RuntimeError).830831Вызов `freeze_support()` не имеет эффекта, если метод запуска не является *spawn*. Кроме того, если модуль выполняется обычным образом интерпретатором Python (программа не была заморожена), то `freeze_support()` не имеет эффекта.832833#### `multiprocessing.get_all_start_methods()`834835Возвращает список поддерживаемых методов запуска, первый из которых является методом по умолчанию. Возможные методы запуска: `'fork'`, `'spawn'` и `'forkserver'`. Не все платформы поддерживают все методы. См. [Контексты и методы запуска](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-start-methods).836837Добавлено в версии 3.4.838839#### `multiprocessing.get_context(method=None)`840841Возвращает объект контекста, который имеет те же атрибуты, что и модуль `multiprocessing`.842843Если *method* равно `None`, то возвращается контекст по умолчанию. Обратите внимание, что если глобальный метод запуска не был установлен, то он будет установлен в системный по умолчанию. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для подробностей. В противном случае *method* должно быть `'fork'`, `'spawn'`, `'forkserver'`. [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError) возникает, если указанный метод запуска недоступен. См. [Контексты и методы запуска](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-start-methods).844845Добавлено в версии 3.4.846847#### `multiprocessing.get_start_method(allow_none=False)`848849Возвращает имя метода запуска, используемого для запуска процессов.850851Если глобальный метод запуска не установлен и *allow\_none* равно `False`, то глобальный метод запуска устанавливается в значение по умолчанию, и возвращается его имя. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для подробностей.852853Возвращаемое значение может быть `'fork'`, `'spawn'`, `'forkserver'` или `None`. См. [Контексты и методы запуска](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-start-methods).854855Добавлено в версии 3.4.856857Изменено в версии 3.8: В macOS метод запуска *spawn* теперь используется по умолчанию. Метод запуска *fork* следует считать небезопасным, так как он может привести к краху подпроцесса. См. [bpo-33725](https://python-all.ru/3/library/multiprocessing.html).858859#### `multiprocessing.set_executable(executable)`860861Устанавливает путь к интерпретатору Python, который будет использоваться при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/3/library/sys.html#sys.executable)). Встраивающим Python, вероятно, потребуется сделать что-то вроде862863```python864set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))865```866867прежде чем они смогут создавать дочерние процессы.868869Изменено в версии 3.4: Теперь поддерживается в POSIX, когда используется метод запуска `'spawn'`.870871Изменено в версии 3.11: Принимает [path-like object](https://python-all.ru/3/glossary.html#term-path-like-object).872873#### `multiprocessing.set_forkserver_preload(module_names)`874875Устанавливает список имен модулей для главного процесса forkserver, чтобы он попытался импортировать их, чтобы их уже импортированное состояние было унаследовано форкнутыми процессами. Любые [`ImportError`](https://python-all.ru/3/library/exceptions.html#ImportError) при этом игнорируются молча. Это можно использовать для повышения производительности, чтобы избежать повторной работы в каждом процессе.876877Чтобы это работало, он должен быть вызван до запуска процесса forkserver (до создания `Pool` или запуска [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process)).878879Имеет смысл только при использовании метода запуска `'forkserver'`. См. [Контексты и методы запуска](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-start-methods).880881Добавлено в версии 3.4.882883#### `multiprocessing.set_start_method(method, force=False)`884885Устанавливает метод, который должен использоваться для запуска дочерних процессов. Аргумент *method* может быть `'fork'`, `'spawn'` или `'forkserver'`. Вызывает [`RuntimeError`](https://python-all.ru/3/library/exceptions.html#RuntimeError), если метод запуска уже был установлен и *force* не равно `True`. Если *method* равно `None` и *force* равно `True`, то метод запуска устанавливается в `None`. Если *method* равно `None` и *force* равно `False`, то контекст устанавливается в контекст по умолчанию.886887Обратите внимание, что эта функция должна вызываться не более одного раза, и она должна быть защищена внутри блока `if __name__ == '__main__'` главного модуля.888889См. [Контексты и методы запуска](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-start-methods).890891Добавлено в версии 3.4.892893> **Примечание**894>895> `multiprocessing` не содержит аналогов [`threading.active_count()`](https://python-all.ru/3/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/3/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/3/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/3/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/3/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/3/library/threading.html#threading.local).896897### Объекты подключения898899Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.900901Объекты подключения обычно создаются с помощью [`Pipe`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Pipe) – см. также [Слушатели и клиенты](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-listeners-clients).902903#### `class multiprocessing.connection.Connection`904905#### `send(obj)`906907Отправляет объект на другой конец подключения; его следует читать с помощью [`recv()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.recv).908909Объект должен быть сериализуемым. Очень большие сериализованные объекты (примерно от 32 МиБ, хотя зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError).910911#### `recv()`912913Возвращает объект, отправленный с другого конца подключения с помощью [`send()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.send). Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/3/library/exceptions.html#EOFError), если больше нечего получать и другой конец был закрыт.914915#### `fileno()`916917Возвращает файловый дескриптор или дескриптор (handle), используемый подключением.918919#### `close()`920921Закрывает подключение.922923Вызывается автоматически при сборке мусора для подключения.924925#### `poll([timeout])`926927Возвращает, есть ли данные для чтения.928929Если *timeout* не указан, то метод возвращается немедленно. Если *timeout* – число, то оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, то используется бесконечное ожидание.930931Обратите внимание: с помощью [`multiprocessing.connection.wait()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.wait) можно одновременно опрашивать несколько объектов подключения.932933#### `send_bytes(buf[, offset[, size]])`934935Отправляет байтовые данные из [байтоподобного объекта](https://python-all.ru/3/glossary.html#term-bytes-like-object) как полное сообщение.936937Если указан *offset*, данные читаются с этой позиции в *buf*. Если указан *size*, то из *buf* будет прочитано указанное количество байт. Очень большие буферы (примерно от 32 МиБ, хотя зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError)938939#### `recv_bytes([maxlength])`940941Возвращает полное сообщение из байтовых данных, отправленное с другого конца подключения, в виде строки. Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/3/library/exceptions.html#EOFError), если больше нечего получать и другой конец закрыт.942943Если указан *maxlength* и сообщение длиннее *maxlength*, то вызывается [`OSError`](https://python-all.ru/3/library/exceptions.html#OSError), и подключение больше не будет доступно для чтения.944945Изменено в версии 3.3: Ранее эта функция вызывала [`IOError`](https://python-all.ru/3/library/exceptions.html#IOError), которая теперь является псевдонимом [`OSError`](https://python-all.ru/3/library/exceptions.html#OSError).946947#### `recv_bytes_into(buf[, offset])`948949Читает в *buf* полное сообщение из байтовых данных, отправленное с другого конца подключения, и возвращает количество байт в сообщении. Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/3/library/exceptions.html#EOFError), если больше нечего получать и другой конец был закрыт.950951*buf* должен быть доступным для записи [байтоподобным объектом](https://python-all.ru/3/glossary.html#term-bytes-like-object). Если указан *offset*, сообщение будет записано в буфер, начиная с этой позиции. Смещение должно быть неотрицательным целым числом, меньшим длины *buf* (в байтах).952953Если буфер слишком мал, то вызывается исключение `BufferTooShort` и полное сообщение доступно как `e.args[0]`, где `e` экземпляр исключения.954955Изменено в версии 3.3: Теперь объекты подключения могут передаваться между процессами с помощью [`Connection.send()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.send) и [`Connection.recv()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.recv).956957Объекты подключения также теперь поддерживают протокол менеджера контекста – см. [Типы менеджеров контекста](https://python-all.ru/3/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__enter__) возвращает объект подключения, а [`__exit__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__exit__) вызывает [`close()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.close).958959Например:960961```pycon962>>> from multiprocessing import Pipe963>>> a, b = Pipe()964>>> a.send([1, 'hello', None])965>>> b.recv()966[1, 'hello', None]967>>> b.send_bytes(b'thank you')968>>> a.recv_bytes()969b'thank you'970>>> import array971>>> arr1 = array.array('i', range(5))972>>> arr2 = array.array('i', [0] * 10)973>>> a.send_bytes(arr1)974>>> count = b.recv_bytes_into(arr2)975>>> assert count == len(arr1) * arr1.itemsize976>>> arr2977array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])978```979980> **Предупреждение**981>982> Метод [`Connection.recv()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.recv) автоматически десериализует полученные данные, что может быть угрозой безопасности, если нельзя доверять процессу, отправившему сообщение.983>984> Поэтому, если объект подключения не был создан с помощью `Pipe()` следует использовать методы [`recv()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.recv) и [`send()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.send) только после выполнения некоторой аутентификации. См. [Ключи аутентификации](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-auth-keys).985986> **Предупреждение**987>988> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.989990### Примитивы синхронизации991992В целом, в многопроцессных программах примитивы синхронизации требуются не так часто, как в многопоточных. См. документацию модуля [`threading`](https://python-all.ru/3/library/threading.html#module-threading).993994Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [Менеджеры](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-managers).995996#### `class multiprocessing.Barrier(parties[, action[, timeout]])`997998Объект барьера: клон [`threading.Barrier`](https://python-all.ru/3/library/threading.html#threading.Barrier).9991000Создание экземпляра этого класса может установить глобальный метод запуска. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для подробностей.10011002Добавлено в версии 3.3.10031004#### `class multiprocessing.BoundedSemaphore([value])`10051006Объект ограниченного семафора: близкий аналог [`threading.BoundedSemaphore`](https://python-all.ru/3/library/threading.html#threading.BoundedSemaphore).10071008Создание экземпляра этого класса может установить глобальный метод запуска. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для подробностей.10091010Единственное отличие от его близкого аналога: первый аргумент метода `acquire` называется *block*, что согласуется с [`Lock.acquire()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock.acquire).10111012#### `locked()`10131014Возвращает логическое значение, указывающее, заблокирован ли данный объект в данный момент.10151016Добавлено в версии 3.14.10171018> **Примечание**1019>1020> На macOS это неотличимо от [`Semaphore`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Semaphore), так как `sem_getvalue()` не реализован на этой платформе.10211022#### `class multiprocessing.Condition([lock])`10231024Условная переменная: псевдоним для [`threading.Condition`](https://python-all.ru/3/library/threading.html#threading.Condition).10251026Если указана *блокировка*, то она должна быть объектом [`Lock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.RLock) из `multiprocessing`.10271028Создание экземпляра этого класса может установить глобальный метод запуска. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для подробностей.10291030Изменено в версии 3.3: Добавлен метод [`wait_for()`](https://python-all.ru/3/library/threading.html#threading.Condition.wait_for).10311032#### `class multiprocessing.Event`10331034Клон [`threading.Event`](https://python-all.ru/3/library/threading.html#threading.Event).10351036Создание экземпляра этого класса может установить глобальный метод запуска. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для подробностей.10371038#### `class multiprocessing.Lock`10391040Нерекурсивный объект блокировки: близкий аналог [`threading.Lock`](https://python-all.ru/3/library/threading.html#threading.Lock). Когда процесс или поток захватывает блокировку, последующие попытки захватить её из любого процесса или потока будут блокироваться до её освобождения; любой процесс или поток может её освободить. Концепции и поведение `threading.Lock` применительно к потокам воспроизводятся здесь в `multiprocessing.Lock` применительно к процессам или потокам, за исключением отмеченных случаев.10411042Обратите внимание, что `Lock` на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.Lock`, инициализированный контекстом по умолчанию.10431044Создание экземпляра этого класса может установить глобальный метод запуска. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для подробностей.10451046`Lock` поддерживает протокол [менеджера контекста](https://python-all.ru/3/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/3/reference/compound_stmts.html#with).10471048#### `acquire(block=True, timeout=None)`10491050Захватывает блокировку (блокирующий или неблокирующий режим).10511052Если аргумент *block* установлен в `True` (по умолчанию), вызов метода будет блокироваться, пока блокировка не окажется в разблокированном состоянии, затем установит её в заблокированное состояние и вернёт `True`. Обратите внимание, что имя этого первого аргумента отличается от имени в [`threading.Lock.acquire()`](https://python-all.ru/3/library/threading.html#threading.Lock.acquire).10531054Если аргумент *block* установлен в `False`, вызов метода не блокируется. Если блокировка в данный момент находится в заблокированном состоянии, возвращается `False`; в противном случае блокировка устанавливается в заблокированное состояние и возвращается `True`.10551056При вызове с положительным значением с плавающей точкой для *timeout* блокировка выполняется не дольше числа секунд, указанного в *timeout*, пока блокировку не удастся захватить. Вызовы с отрицательным значением *timeout* эквивалентны *timeout*, равному нулю. Вызовы со значением *timeout* равным `None` (по умолчанию) устанавливают бесконечный период ожидания. Обратите внимание, что обработка отрицательных или `None` значений для *timeout* отличается от реализованного поведения в [`threading.Lock.acquire()`](https://python-all.ru/3/library/threading.html#threading.Lock.acquire). Аргумент *timeout* не имеет практического значения, если аргумент *block* установлен в `False`, и поэтому игнорируется. Возвращает `True`, если блокировка была захвачена, или `False`, если период ожидания истёк.10571058#### `release()`10591060Освобождает блокировку. Может вызываться из любого процесса или потока, а не только из того, который изначально захватил блокировку.10611062Поведение аналогично [`threading.Lock.release()`](https://python-all.ru/3/library/threading.html#threading.Lock.release) за исключением того, что при вызове на незаблокированной блокировке возбуждается [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError).10631064#### `locked()`10651066Возвращает булево значение, указывающее, заблокирован ли данный объект в данный момент.10671068Добавлено в версии 3.14.10691070#### `class multiprocessing.RLock`10711072Объект рекурсивной блокировки: близкий аналог [`threading.RLock`](https://python-all.ru/3/library/threading.html#threading.RLock). Рекурсивная блокировка должна быть освобождена тем же процессом или потоком, который её захватил. После того как процесс или поток захватил рекурсивную блокировку, тот же процесс или поток может захватить её снова без блокировки; этот процесс или поток должен освободить её по одному разу за каждый захват.10731074Обратите внимание, что `RLock` на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.RLock`, инициализированный контекстом по умолчанию.10751076Создание экземпляра этого класса может установить глобальный метод запуска. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для получения дополнительных сведений.10771078`RLock` поддерживает протокол [менеджера контекста](https://python-all.ru/3/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/3/reference/compound_stmts.html#with).10791080#### `acquire(block=True, timeout=None)`10811082Захватывает блокировку, блокирующую или неблокирующую.10831084При вызове с аргументом *block*, установленным в `True`, блокируется до тех пор, пока блокировка не окажется в разблокированном состоянии (не принадлежит ни одному процессу или потоку), если только блокировка уже не принадлежит текущему процессу или потоку. Затем текущий процесс или поток получает владение блокировкой (если ещё не владеет ею), и уровень рекурсии внутри блокировки увеличивается на единицу, возвращая значение `True`. Обратите внимание, что имеется несколько различий в поведении этого первого аргумента по сравнению с реализацией [`threading.RLock.acquire()`](https://python-all.ru/3/library/threading.html#threading.RLock.acquire), начиная с названия самого аргумента.10851086При вызове с аргументом *block*, установленным в `False`, не блокируется. Если блокировка уже была захвачена (и, следовательно, принадлежит) другим процессом или потоком, текущий процесс или поток не получает владение, а уровень рекурсии внутри блокировки не изменяется, возвращая значение `False`. Если блокировка находится в разблокированном состоянии, текущий процесс или поток получает владение, а уровень рекурсии увеличивается, возвращая значение `True`.10871088Использование и поведение аргумента *timeout* такие же, как в [`Lock.acquire()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock.acquire). Обратите внимание, что некоторые из этих особенностей *timeout* отличаются от реализованного поведения в [`threading.RLock.acquire()`](https://python-all.ru/3/library/threading.html#threading.RLock.acquire).10891090#### `release()`10911092Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения уровень рекурсии становится нулевым, переводит блокировку в разблокированное состояние (не принадлежит ни одному процессу или потоку), и если какие-либо другие процессы или потоки ожидают разблокировки, позволяет ровно одному из них продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся заблокированной и принадлежащей вызывающему процессу или потоку.10931094Вызывайте этот метод только когда вызывающий процесс или поток владеет блокировкой. Исключение [`AssertionError`](https://python-all.ru/3/library/exceptions.html#AssertionError) возбуждается, если этот метод вызывается процессом или потоком, не являющимся владельцем, или если блокировка находится в разблокированном (непринадлежащем) состоянии. Обратите внимание, что тип возбуждаемого исключения в этой ситуации отличается от реализованного поведения в [`threading.RLock.release()`](https://python-all.ru/3/library/threading.html#threading.RLock.release).10951096#### `locked()`10971098Возвращает булево значение, указывающее, заблокирован ли данный объект в данный момент.10991100Добавлено в версии 3.14.11011102#### `class multiprocessing.Semaphore([value])`11031104Объект семафора: близкий аналог [`threading.Semaphore`](https://python-all.ru/3/library/threading.html#threading.Semaphore).11051106Создание экземпляра этого класса может установить глобальный метод запуска. См. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method) для получения дополнительных сведений.11071108Имеется единственное отличие от его близкого аналога: первый аргумент метода `acquire` называется *block*, что согласуется с [`Lock.acquire()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock.acquire).11091110#### `get_value()`11111112Возвращает текущее значение семафора.11131114Обратите внимание, что это может возбудить [`NotImplementedError`](https://python-all.ru/3/library/exceptions.html#NotImplementedError) на платформах, подобных macOS, где `sem_getvalue()` не реализован.11151116#### `locked()`11171118Возвращает булево значение, указывающее, заблокирован ли данный объект в данный момент.11191120Добавлено в версии 3.14.11211122> **Примечание**1123>1124> На macOS `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с тайм-аутом будет эмулировать поведение этой функции с помощью цикла ожидания.11251126> **Примечание**1127>1128> Некоторые функции этого пакета требуют работающей реализации общего семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки импортировать его приведут к [`ImportError`](https://python-all.ru/3/library/exceptions.html#ImportError). См. [bpo-3770](https://python-all.ru/3/library/multiprocessing.html) для получения дополнительной информации.11291130### Общие [`ctypes`](https://python-all.ru/3/library/ctypes.html#module-ctypes) объекты11311132Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.11331134#### `multiprocessing.Value(typecode_or_type, *args, lock=True)`11351136Возвращает объект [`ctypes`](https://python-all.ru/3/library/ctypes.html#module-ctypes), выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для объекта. Сам объект можно получить через атрибут *value* объекта [`Value`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Value).11371138*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3/library/array.html#module-array). *\*args* передаётся конструктору типа.11391140Если *блокировка* равна `True` (по умолчанию), то создаётся новый рекурсивный объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".11411142Операции, подобные `+=`, которые включают чтение и запись, не являются атомарными. Поэтому, если, например, требуется атомарно увеличить разделяемое значение, недостаточно просто сделать11431144```python1145counter.value += 11146```11471148При условии, что связанная блокировка рекурсивна (по умолчанию она такой и является), можно вместо этого сделать11491150```python1151with counter.get_lock():1152    counter.value += 11153```11541155Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.11561157#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`11581159Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.11601161*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо [тип ctypes](https://python-all.ru/3/library/ctypes.html#ctypes-fundamental-data-types), либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3/library/array.html#module-array), за исключением `'w'`, который не поддерживается. Кроме того, код типа `'c'` является псевдонимом [`ctypes.c_char`](https://python-all.ru/3/library/ctypes.html#ctypes.c_char). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.11621163Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".11641165Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.11661167Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые можно использовать для сохранения и извлечения байтовых строк. В то время как *raw* позволяет взаимодействовать с объектом [`bytes`](https://python-all.ru/3/library/stdtypes.html#bytes) на полный размер массива, чтение *value* завершится после нулевого байта, как это делается со строками в большинстве языков программирования.11681169#### Модуль `multiprocessing.sharedctypes`11701171Модуль `multiprocessing.sharedctypes` предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/3/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.11721173> **Примечание**1174>1175> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.11761177#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`11781179Возвращает массив ctypes, выделенный из разделяемой памяти.11801181*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.11821183Обратите внимание, что установка и получение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.11841185#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`11861187Возвращает объект ctypes, выделенный из разделяемой памяти.11881189*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/3/library/array.html#module-array). *\*args* передаётся конструктору типа.11901191Обратите внимание, что установка и получение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.11921193Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/3/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют использовать его для сохранения и извлечения строк – см. документацию к [`ctypes`](https://python-all.ru/3/library/ctypes.html#module-ctypes).11941195#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *, lock=True, ctx=None)`11961197То же, что и [`RawArray()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного массива ctypes.11981199Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".12001201*ctx* – это контекстный объект или `None` (используется текущий контекст). Если `None`, вызов этого может установить глобальный метод запуска. Подробнее см. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method).12021203Обратите внимание, что *блокировка* и *ctx* – это параметры, передаваемые только по ключевому слову.12041205#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args, lock=True, ctx=None)`12061207То же, что и [`RawValue()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного объекта ctypes.12081209Если *блокировка* равен `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* – объект [`Lock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равен `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».12101211*ctx* – это объект контекста или `None` (использовать текущий контекст). Если `None`, вызов этого метода может установить глобальный метод запуска. Подробнее см. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method).12121213Обратите внимание, что *блокировка* и *ctx* – это только именованные параметры.12141215#### `multiprocessing.sharedctypes.copy(obj)`12161217Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes *obj*.12181219#### `multiprocessing.sharedctypes.synchronized(obj, lock=None, ctx=None)`12201221Возвращает процесс-безопасную обёртку для объекта ctypes, которая использует *блокировка* для синхронизации доступа. Если *блокировка* равен `None` (по умолчанию), объект [`multiprocessing.RLock`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.RLock) создаётся автоматически.12221223*ctx* – это объект контекста или `None` (использовать текущий контекст). Если `None`, вызов этого метода может установить глобальный метод запуска. Подробнее см. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method).12241225Синхронизированная обёртка будет иметь два дополнительных метода (помимо методов обёрнутого объекта): `get_obj()` возвращает обёрнутый объект, а `get_lock()` – объект блокировки, используемый для синхронизации.12261227Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.12281229Изменено в версии 3.5: Синхронизированные объекты поддерживают протокол [контекстного менеджера](https://python-all.ru/3/glossary.html#term-context-manager).12301231В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некоторый подкласс [`ctypes.Structure`](https://python-all.ru/3/library/ctypes.html#ctypes.Structure).)12321233| ctypes | sharedctypes с типом | sharedctypes с typecode |1234| --- | --- | --- |1235| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |1236| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) |  |1237| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |1238| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |12391240Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:12411242```python1243from multiprocessing import Process, Lock1244from multiprocessing.sharedctypes import Value, Array1245from ctypes import Structure, c_double12461247class Point(Structure):1248    _fields_ = [('x', c_double), ('y', c_double)]12491250def modify(n, x, s, A):1251    n.value **= 21252    x.value **= 21253    s.value = s.value.upper()1254    for a in A:1255        a.x **= 21256        a.y **= 212571258if __name__ == '__main__':1259    lock = Lock()12601261    n = Value('i', 7)1262    x = Value(c_double, 1.0/3.0, lock=False)1263    s = Array('c', b'hello world', lock=lock)1264    A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)12651266    p = Process(target=modify, args=(n, x, s, A))1267    p.start()1268    p.join()12691270    print(n.value)1271    print(x.value)1272    print(s.value)1273    print([(a.x, a.y) for a in A])1274```12751276Выводятся следующие результаты:12771278```text12794912800.11111111111111111281HELLO WORLD1282[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]1283```12841285### Менеджеры12861287Менеджеры предоставляют способ создания данных, которыми можно обмениваться между разными процессами, в том числе по сети между процессами, выполняющимися на разных машинах. Объект менеджера управляет серверным процессом, который управляет *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.12881289#### `multiprocessing.Manager()`12901291Возвращает запущенный объект [`SyncManager`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.SyncManager), который можно использовать для совместного использования объектов между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.12921293Процессы менеджера завершаются, как только они будут собраны сборщиком мусора или завершится их родительский процесс. Классы менеджера определены в модуле [`multiprocessing.managers`](https://python-all.ru/3/library/multiprocessing.html#module-multiprocessing.managers):12941295#### `class multiprocessing.managers.BaseManager(address=None, authkey=None, serializer='pickle', ctx=None, *, shutdown_timeout=1.0)`12961297Создает объект BaseManager.12981299После создания необходимо вызвать [`start()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или `get_server().serve_forever()`, чтобы объект менеджера ссылался на запущенный процесс менеджера.13001301*address* – адрес, на котором процесс менеджера ожидает новые подключения. Если *address* равно `None`, то выбирается произвольный адрес.13021303*authkey* – ключ аутентификации, который будет использоваться для проверки подлинности входящих подключений к серверному процессу. Если *authkey* равно `None`, используется `current_process().authkey`. В противном случае используется *authkey*, и он должен быть байтовой строкой.13041305*serializer* должен быть `'pickle'` (использовать сериализацию [`pickle`](https://python-all.ru/3/library/pickle.html#module-pickle)) или `'xmlrpclib'` (использовать сериализацию [`xmlrpc.client`](https://python-all.ru/3/library/xmlrpc.client.html#module-xmlrpc.client)).13061307*ctx* – это объект контекста или `None` (использовать текущий контекст). Если `None`, вызов этого может установить глобальный метод запуска. Подробнее см. [Глобальный метод запуска](https://python-all.ru/3/library/multiprocessing.html#global-start-method).13081309*shutdown\_timeout* – тайм-аут в секундах, который используется для ожидания завершения процесса, используемого менеджером, в методе [`shutdown()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager.shutdown). Если тайм-аут истекает, процесс завершается. Если завершение процесса также истекает по тайм-ауту, процесс уничтожается.13101311Изменено в версии 3.11: Добавлен параметр *shutdown\_timeout*.13121313#### `start([initializer[, initargs]])`13141315Запускает подпроцесс для запуска менеджера. Если *initializer* не равно `None`, то подпроцесс вызовет `initializer(*initargs)` при запуске.13161317#### `get_server()`13181319Возвращает объект `Server`, представляющий реальный сервер, управляемый менеджером. Объект `Server` поддерживает метод `serve_forever()`:13201321```python1322>>> from multiprocessing.managers import BaseManager1323>>> manager = BaseManager(address=('', 50000), authkey=b'abc')1324>>> server = manager.get_server()1325>>> server.serve_forever()1326```13271328`Server` также имеет атрибут [`address`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).13291330#### `connect()`13311332Подключает локальный объект менеджера к удаленному процессу менеджера:13331334```python1335>>> from multiprocessing.managers import BaseManager1336>>> m = BaseManager(address=('127.0.0.1', 50000), authkey=b'abc')1337>>> m.connect()1338```13391340#### `shutdown()`13411342Останавливает процесс, используемый менеджером. Это доступно только если [`start()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.13431344Этот метод можно вызывать несколько раз.13451346#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`13471348Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.13491350*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.13511352*callable* – это вызываемый объект, используемый для создания объектов для данного идентификатора типа. Если экземпляр менеджера будет подключен к серверу с помощью метода [`connect()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager.connect), или если аргумент *create\_method* равен `False`, то этот параметр можно оставить как `None`.13531354*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.13551356*exposed* используется для указания последовательности имен методов, к которым прокси для данного typeid должны иметь доступ через [`BaseProxy._callmethod()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). (Если *exposed* равно `None`, то вместо него используется `proxytype._exposed_`, если он существует.) Если список exposed не указан, то все «публичные методы» разделяемого объекта будут доступны. (Здесь «публичный метод» означает любой атрибут, у которого есть метод [`__call__()`](https://python-all.ru/3/reference/datamodel.html#object.__call__) и имя которого не начинается с `'_'`.)13571358*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно отображает имена методов в строки typeid. (Если *method\_to\_typeid* равно `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом этого отображения или отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.13591360*create\_method* определяет, следует ли создать метод с именем *typeid*, который можно использовать, чтобы указать серверному процессу создать новый разделяемый объект и вернуть для него прокси. По умолчанию `True`.13611362Экземпляры `BaseManager` также имеют одно свойство только для чтения:13631364#### `address`13651366Адрес, используемый менеджером.13671368Изменено в версии 3.3: Объекты Manager поддерживают протокол управления контекстом – см. [Типы менеджеров контекста](https://python-all.ru/3/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__enter__) запускает серверный процесс (если он еще не запущен), а затем возвращает объект менеджера. [`__exit__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__exit__) вызывает [`shutdown()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager.shutdown).13691370В предыдущих версиях [`__enter__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__enter__) не запускал серверный процесс менеджера, если он ещё не был запущен.13711372#### `class multiprocessing.managers.SyncManager`13731374Подкласс [`BaseManager`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются [`multiprocessing.Manager()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Manager).13751376Его методы создают и возвращают [объекты-прокси](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-proxy-objects) для ряда часто используемых типов данных, которые нужно синхронизировать между процессами. Сюда в первую очередь входят общие списки и словари.13771378#### `Barrier(parties[, action[, timeout]])`13791380Создать общий объект [`threading.Barrier`](https://python-all.ru/3/library/threading.html#threading.Barrier) и вернуть для него прокси.13811382Добавлено в версии 3.3.13831384#### `BoundedSemaphore([value])`13851386Создать общий объект [`threading.BoundedSemaphore`](https://python-all.ru/3/library/threading.html#threading.BoundedSemaphore) и вернуть для него прокси.13871388#### `Condition([lock])`13891390Создать общий объект [`threading.Condition`](https://python-all.ru/3/library/threading.html#threading.Condition) и вернуть прокси для него.13911392Если *блокировка* предоставлена, то она должна быть прокси для объекта [`threading.Lock`](https://python-all.ru/3/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/3/library/threading.html#threading.RLock).13931394Изменено в версии 3.3: Был добавлен метод [`wait_for()`](https://python-all.ru/3/library/threading.html#threading.Condition.wait_for).13951396#### `Event()`13971398Создать общий объект [`threading.Event`](https://python-all.ru/3/library/threading.html#threading.Event) и вернуть для него прокси.13991400#### `Lock()`14011402Создать общий объект [`threading.Lock`](https://python-all.ru/3/library/threading.html#threading.Lock) и вернуть для него прокси.14031404#### `Namespace()`14051406Создать общий объект [`Namespace`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.Namespace) и вернуть для него прокси.14071408#### `Queue([maxsize])`14091410Создать общий объект [`queue.Queue`](https://python-all.ru/3/library/queue.html#queue.Queue) и вернуть для него прокси.14111412#### `RLock()`14131414Создать общий объект [`threading.RLock`](https://python-all.ru/3/library/threading.html#threading.RLock) и вернуть для него прокси.14151416#### `Semaphore([value])`14171418Создать общий объект [`threading.Semaphore`](https://python-all.ru/3/library/threading.html#threading.Semaphore) и вернуть прокси для него.14191420#### `Array(typecode, sequence)`14211422Создать массив и вернуть для него прокси.14231424#### `Value(typecode, value)`14251426Создать объект с атрибутом `value`, доступным для записи, и вернуть прокси для него.14271428#### `dict()`14291430#### `dict(mapping)`14311432#### `dict(sequence)`14331434Создать общий объект [`dict`](https://python-all.ru/3/library/stdtypes.html#dict) и вернуть для него прокси.14351436#### `list()`14371438#### `list(sequence)`14391440Создать общий объект [`list`](https://python-all.ru/3/library/stdtypes.html#list) и вернуть прокси для него.14411442#### `set()`14431444#### `set(sequence)`14451446#### `set(mapping)`14471448Создать общий объект [`set`](https://python-all.ru/3/library/stdtypes.html#set) и вернуть прокси для него.14491450Добавлено в версии 3.14: [`set`](https://python-all.ru/3/library/stdtypes.html#set) добавлена поддержка.14511452Изменено в версии 3.6: Общие объекты могут быть вложенными. Например, общий объект-контейнер, такой как общий список, может содержать другие общие объекты, и все они будут управляться и синхронизироваться `SyncManager`.14531454#### `class multiprocessing.managers.Namespace`14551456Тип, который можно зарегистрировать с помощью [`SyncManager`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.SyncManager).14571458Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.14591460Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:14611462```pycon1463>>> mp_context = multiprocessing.get_context('spawn')1464>>> manager = mp_context.Manager()1465>>> Global = manager.Namespace()1466>>> Global.x = 101467>>> Global.y = 'hello'1468>>> Global._z = 12.3    # это атрибут прокси1469>>> print(Global)1470Namespace(x=10, y='hello')1471```14721473#### Настраиваемые менеджеры14741475Чтобы создать собственный менеджер, создаётся подкласс [`BaseManager`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager) и используется метод класса [`register()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:14761477```python1478from multiprocessing.managers import BaseManager14791480class MathsClass:1481    def add(self, x, y):1482        return x + y1483    def mul(self, x, y):1484        return x * y14851486class MyManager(BaseManager):1487    pass14881489MyManager.register('Maths', MathsClass)14901491if __name__ == '__main__':1492    with MyManager() as manager:1493        maths = manager.Maths()1494        print(maths.add(4, 3))         # выводит 71495        print(maths.mul(7, 8))         # выводит 561496```14971498#### Использование удалённого менеджера14991500Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).15011502Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:15031504```python1505>>> from multiprocessing.managers import BaseManager1506>>> from queue import Queue1507>>> queue = Queue()1508>>> class QueueManager(BaseManager): pass1509>>> QueueManager.register('get_queue', callable=lambda:queue)1510>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1511>>> s = m.get_server()1512>>> s.serve_forever()1513```15141515Один клиент может получить доступ к серверу следующим образом:15161517```python1518>>> from multiprocessing.managers import BaseManager1519>>> class QueueManager(BaseManager): pass1520>>> QueueManager.register('get_queue')1521>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1522>>> m.connect()1523>>> queue = m.get_queue()1524>>> queue.put('hello')1525```15261527Другой клиент также может использовать его:15281529```python1530>>> from multiprocessing.managers import BaseManager1531>>> class QueueManager(BaseManager): pass1532>>> QueueManager.register('get_queue')1533>>> m = QueueManager(address=('foo.bar.org', 50000), authkey=b'abracadabra')1534>>> m.connect()1535>>> queue = m.get_queue()1536>>> queue.get()1537'hello'1538```15391540Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:15411542```python1543>>> from multiprocessing import Process, Queue1544>>> from multiprocessing.managers import BaseManager1545>>> class Worker(Process):1546...     def __init__(self, q):1547...         self.q = q1548...         super().__init__()1549...     def run(self):1550...         self.q.put('local hello')1551...1552>>> queue = Queue()1553>>> w = Worker(queue)1554>>> w.start()1555>>> class QueueManager(BaseManager): pass1556...1557>>> QueueManager.register('get_queue', callable=lambda: queue)1558>>> m = QueueManager(address=('', 50000), authkey=b'abracadabra')1559>>> s = m.get_server()1560>>> s.serve_forever()1561```15621563### Объекты-прокси15641565Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.15661567Объект-прокси имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Таким образом, прокси можно использовать так же, как и его референт:15681569```pycon1570>>> mp_context = multiprocessing.get_context('spawn')1571>>> manager = mp_context.Manager()1572>>> l = manager.list([i*i for i in range(10)])1573>>> print(l)1574[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1575>>> print(repr(l))1576<ListProxy object, typeid 'list' at 0x...>1577>>> l[4]1578161579>>> l[2:5]1580[4, 9, 16]1581```15821583Обратите внимание, что применение [`str()`](https://python-all.ru/3/library/stdtypes.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/3/library/functions.html#repr) вернёт представление прокси.15841585Важная особенность объектов-прокси – они сериализуемы (picklable), поэтому их можно передавать между процессами. Таким образом, референт может содержать [Объекты-прокси](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-proxy-objects). Это допускает вложение управляемых списков, словарей и других [Объектов-прокси](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-proxy-objects):15861587```pycon1588>>> a = manager.list()1589>>> b = manager.list()1590>>> a.append(b)         # референт a теперь содержит референт b1591>>> print(a, b)1592[<ListProxy object, typeid 'list' at ...>] []1593>>> b.append('hello')1594>>> print(a[0], b)1595['hello'] ['hello']1596```15971598Аналогично, прокси словарей и списков могут быть вложены друг в друга:15991600```python1601>>> l_outer = manager.list([ manager.dict() for i in range(2) ])1602>>> d_first_inner = l_outer[0]1603>>> d_first_inner['a'] = 11604>>> d_first_inner['b'] = 21605>>> l_outer[1]['c'] = 31606>>> l_outer[1]['z'] = 261607>>> print(l_outer[0])1608{'a': 1, 'b': 2}1609>>> print(l_outer[1])1610{'c': 3, 'z': 26}1611```16121613Если в референт входят стандартные (не прокси) объекты [`list`](https://python-all.ru/3/library/stdtypes.html#list) или [`dict`](https://python-all.ru/3/library/stdtypes.html#dict), изменения этих изменяемых значений не будут распространяться через менеджер, потому что прокси не может узнать, когда значения, содержащиеся внутри, изменяются. Однако сохранение значения в прокси-контейнере (что вызывает `__setitem__` на объекте-прокси) распространяется через менеджер, поэтому для эффективного изменения такого элемента можно присвоить изменённое значение обратно в прокси-контейнер:16141615```python1616# создать прокси для списка и добавить изменяемый объект (словарь)1617lproxy = manager.list()1618lproxy.append({})1619# теперь изменить словарь1620d = lproxy[0]1621d['a'] = 11622d['b'] = 21623# на данный момент изменения в d еще не синхронизированы, но при1624# обновлении словаря прокси уведомляется об изменении1625lproxy[0] = d1626```16271628Этот подход, возможно, менее удобен, чем использование вложенных [Объектов-прокси](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-proxy-objects) в большинстве случаев, но также демонстрирует степень контроля над синхронизацией.16291630> **Примечание**1631>1632> Типы прокси в `multiprocessing` не поддерживают сравнение по значению. Так, например:1633>1634> ```pycon1635> >>> manager.list([1,2,3]) == [1,2,3]1636> False1637> ```1638>1639> При сравнении следует просто использовать копию референта.16401641#### `class multiprocessing.managers.BaseProxy`16421643Прокси-объекты являются экземплярами подклассов `BaseProxy`.16441645#### `_callmethod(methodname[, args[, kwds]])`16461647Вызывает метод референта прокси и возвращает результат.16481649Если `proxy` – это прокси, чей референт – `obj`, то выражение16501651```python1652proxy._callmethod(methodname, args, kwds)1653```16541655вычислит выражение16561657```python1658getattr(obj, methodname)(*args, **kwds)1659```16601661в процессе менеджера.16621663Возвращаемое значение будет копией результата вызова или прокси для нового разделяемого объекта – см. документацию по аргументу *method\_to\_typeid* функции [`BaseManager.register()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).16641665Если при вызове возникло исключение, то оно повторно возбуждается с помощью `_callmethod()`. Если в процессе менеджера возникло другое исключение, то оно преобразуется в исключение `RemoteError` и возбуждается с помощью `_callmethod()`.16661667Обратите внимание, что исключение будет возбуждаться, если *methodname* не был *экспонирован*.16681669Пример использования `_callmethod()`:16701671```pycon1672>>> l = manager.list(range(10))1673>>> l._callmethod('__len__')1674101675>>> l._callmethod('__getitem__', (slice(2, 7),)) # эквивалентно l[2:7]1676[2, 3, 4, 5, 6]1677>>> l._callmethod('__getitem__', (20,))          # эквивалентно l[20]1678Traceback (most recent call last):1679...1680IndexError: list index out of range1681```16821683#### `_getvalue()`16841685Возвращает копию референта.16861687Если референт не может быть сериализован (unpicklable), то это вызовет исключение.16881689#### `__repr__()`16901691Возвращает представление прокси-объекта.16921693#### `__str__()`16941695Возвращает представление референта.16961697#### Очистка16981699Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.17001701Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.17021703### Пулы процессов17041705Можно создать пул процессов, который будет выполнять задачи, отправленные ему с помощью класса [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool).17061707#### `class multiprocessing.pool.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]])`17081709Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.17101711*processes* – количество используемых рабочих процессов. Если *processes* равно `None`, то используется число, возвращаемое [`os.process_cpu_count()`](https://python-all.ru/3/library/os.html#os.process_cpu_count).17121713Если *initializer* не равно `None`, то каждый рабочий процесс вызовет `initializer(*initargs)` при запуске.17141715*maxtasksperchild* – количество задач, которое рабочий процесс может выполнить до завершения и замены новым рабочим процессом, чтобы освободить неиспользуемые ресурсы. Значение по умолчанию *maxtasksperchild* равно `None`, что означает, что рабочие процессы будут жить столько же, сколько и пул.17161717*context* можно использовать для указания контекста, используемого для запуска рабочих процессов. Обычно пул создаётся с помощью функции `multiprocessing.Pool()` или метода [`Pool()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool) объекта контекста. В обоих случаях *context* устанавливается соответствующим образом. Если `None`, вызов этой функции будет иметь побочный эффект: установит текущий глобальный метод запуска, если он ещё не был установлен. См. функцию `get_context()`.17181719Обратите внимание, что методы объекта пула должны вызываться только тем процессом, который создал пул.17201721> **Предупреждение**1722>1723> Объекты [`multiprocessing.pool`](https://python-all.ru/3/library/multiprocessing.html#module-multiprocessing.pool) имеют внутренние ресурсы, которые необходимо должным образом управлять (как и любыми другими ресурсами): используйте пул как контекстный менеджер или вызывайте [`close()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.close) и [`terminate()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.terminate) вручную. Несоблюдение этого может привести к зависанию процесса при завершении.1724>1725> Обратите внимание, что **неправильно** полагаться на сборщик мусора для уничтожения пула, поскольку CPython не гарантирует вызов финализатора пула (см. [`object.__del__()`](https://python-all.ru/3/reference/datamodel.html#object.__del__) для получения дополнительной информации).17261727Изменено в версии 3.2: Добавлен параметр *maxtasksperchild*.17281729Изменено в версии 3.4: Добавлен параметр *context*.17301731Изменено в версии 3.13: *processes* по умолчанию использует [`os.process_cpu_count()`](https://python-all.ru/3/library/os.html#os.process_cpu_count) вместо [`os.cpu_count()`](https://python-all.ru/3/library/os.html#os.cpu_count).17321733> **Примечание**1734>1735> Рабочие процессы внутри `Pool` обычно существуют в течение всего времени работы очереди задач пула. Часто встречающийся в других системах (например, Apache, mod\_wsgi и т.д.) подход для освобождения ресурсов, удерживаемых рабочими процессами, заключается в том, чтобы разрешить рабочему процессу в пуле выполнить только определённое количество работы перед завершением, после чего он очищается и создаётся новый процесс взамен старого. Аргумент *maxtasksperchild* конструктора `Pool` предоставляет эту возможность конечному пользователю.17361737#### `apply(func[, args[, kwds]])`17381739Вызывает *func* с аргументами *args* и именованными аргументами *kwds*. Блокируется до получения результата. Поскольку этот метод блокируется, [`apply_async()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.apply_async) лучше подходит для параллельного выполнения работы. Кроме того, *func* выполняется только в одном из рабочих процессов пула.17401741#### `apply_async(func[, args[, kwds[, callback[, error_callback]]]])`17421743Вариант метода [`apply()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.apply), который возвращает объект [`AsyncResult`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.AsyncResult).17441745Если указан *колбэк*, то это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк*, если только вызов не завершился ошибкой – в этом случае вместо него применяется *колбэк ошибки*.17461747Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.17481749Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.17501751#### `map(func, iterable[, chunksize])`17521753Параллельный аналог встроенной функции [`map()`](https://python-all.ru/3/library/functions.html#map) (однако поддерживает только один аргумент *iterable*, для нескольких итерируемых объектов см. [`starmap()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.starmap)). Блокируется до получения результата.17541755Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.17561757Обратите внимание, что для очень длинных итерируемых объектов это может привести к высокому потреблению памяти. Для повышения эффективности рассмотрите возможность использования [`imap()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.imap) или [`imap_unordered()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.imap_unordered) с явным указанием *chunksize*.17581759#### `map_async(func, iterable[, chunksize[, callback[, error_callback]]])`17601761Вариант метода [`map()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.map), который возвращает объект [`AsyncResult`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.AsyncResult).17621763Если указан *колбэк*, то это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк*, если только вызов не завершился ошибкой – в этом случае вместо него применяется *колбэк ошибки*.17641765Если указан *error\_callback*, то это должен быть вызываемый объект, принимающий один аргумент. Если целевая функция завершается ошибкой, то *error\_callback* вызывается с экземпляром исключения.17661767Колбэки должны завершаться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.17681769#### `imap(func, iterable[, chunksize])`17701771Более ленивая версия [`map()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.map).17721773Аргумент *chunksize* совпадает с тем, что используется в методе [`map()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.map). Для очень длинных итерируемых объектов использование большого значения *chunksize* может привести к завершению задачи **значительно** быстрее, чем при использовании значения по умолчанию `1`.17741775Кроме того, если *chunksize* равно `1`, то метод `next()` итератора, возвращаемого методом `imap()`, имеет необязательный параметр *timeout*: `next(timeout)` возбудит [`multiprocessing.TimeoutError`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.TimeoutError), если результат не может быть возвращён в течение *timeout* секунд.17761777#### `imap_unordered(func, iterable[, chunksize])`17781779То же, что и [`imap()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.imap), за исключением того, что порядок результатов из возвращаемого итератора считается произвольным. (Только когда есть только один рабочий процесс, порядок гарантированно будет «правильным».)17801781#### `starmap(func, iterable[, chunksize])`17821783Как и [`map()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.map), за исключением того, что элементы *iterable* должны быть итерируемыми объектами, которые распаковываются в аргументы.17841785Таким образом, *iterable* из `[(1,2), (3, 4)]` приводит к `[func(1,2), func(3,4)]`.17861787Добавлено в версии 3.3.17881789#### `starmap_async(func, iterable[, chunksize[, callback[, error_callback]]])`17901791Комбинация [`starmap()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.starmap) и [`map_async()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.map_async), которая перебирает *iterable* из итерируемых объектов и вызывает *func* с распакованными итерируемыми объектами. Возвращает объект результата.17921793Добавлено в версии 3.3.17941795#### `close()`17961797Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.17981799#### `terminate()`18001801Немедленно останавливает рабочие процессы, не дожидаясь завершения незаконченных задач. Когда объект пула будет собран сборщиком мусора, `terminate()` будет вызван немедленно.18021803#### `join()`18041805Ожидает завершения рабочих процессов. Перед использованием `join()` необходимо вызвать [`close()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.close) или [`terminate()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.terminate).18061807Изменено в версии 3.3:Объекты пула теперь поддерживают протокол управления контекстом – см. [Context Manager Types](https://python-all.ru/3/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__enter__) возвращает объект пула, а [`__exit__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__exit__) вызывает [`terminate()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.terminate).18081809#### `class multiprocessing.pool.AsyncResult`18101811Класс результата, возвращаемого [`Pool.apply_async()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.apply_async) и [`Pool.map_async()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.map_async).18121813#### `get([timeout])`18141815Возвращает результат, когда он будет получен. Если *timeout* не равен `None` и результат не поступает в течение *timeout* секунд, то вызывается [`multiprocessing.TimeoutError`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.TimeoutError). Если удалённый вызов вызвал исключение, то это исключение будет повторно возбуждено `get()`.18161817#### `wait([timeout])`18181819Ожидает, пока результат не станет доступен или пока не пройдёт *timeout* секунд.18201821#### `ready()`18221823Возвращает, завершён ли вызов.18241825#### `successful()`18261827Возвращает, завершился ли вызов без возникновения исключения. Возбуждает [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError), если результат ещё не готов.18281829Изменено в версии 3.7:Если результат ещё не готов, [`ValueError`](https://python-all.ru/3/library/exceptions.html#ValueError) возбуждается вместо [`AssertionError`](https://python-all.ru/3/library/exceptions.html#AssertionError).18301831Следующий пример демонстрирует использование пула:18321833```python1834from multiprocessing import Pool1835import time18361837def f(x):1838    return x*x18391840if __name__ == '__main__':1841    with Pool(processes=4) as pool:         # запустить 4 рабочих процесса1842        result = pool.apply_async(f, (10,)) # вычислить "f(10)" асинхронно в одном процессе1843        print(result.get(timeout=1))        # выводит "100", если компьютер не *очень* медленный18441845        print(pool.map(f, range(10)))       # выводит "[0, 1, 4,..., 81]"18461847        it = pool.imap(f, range(10))1848        print(next(it))                     # выводит "0"1849        print(next(it))                     # выводит "1"1850        print(it.next(timeout=1))           # выводит "4", если компьютер не *очень* медленный18511852        result = pool.apply_async(time.sleep, (10,))1853        print(result.get(timeout=1))        # вызывает multiprocessing.TimeoutError1854```18551856### Слушатели и клиенты18571858Обычно обмен сообщениями между процессами осуществляется с помощью очередей или объектов [`Connection`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection), возвращаемых [`Pipe()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Pipe).18591860Однако модуль `multiprocessing.connection` обеспечивает дополнительную гибкость. По сути, он предоставляет высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows. Также поддерживается *аутентификация на основе дайджеста* с использованием модуля [`hmac`](https://python-all.ru/3/library/hmac.html#module-hmac) и опрос нескольких подключений одновременно.18611862#### `multiprocessing.connection.deliver_challenge(connection, authkey)`18631864Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.18651866Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.AuthenticationError).18671868#### `multiprocessing.connection.answer_challenge(connection, authkey)`18691870Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.18711872Если приветственное сообщение не получено, то возбуждается [`AuthenticationError`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.AuthenticationError).18731874#### `multiprocessing.connection.Client(address[, family[, authkey]])`18751876Пытается установить соединение с слушателем, использующим адрес *address*, возвращая [`Connection`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection).18771878Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [Address Formats](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-address-formats))18791880Если *authkey* задан и не равен `None`, он должен быть байтовой строкой и будет использоваться в качестве секретного ключа для аутентификации на основе HMAC. Если *authkey* равен `None`, аутентификация не выполняется. При неудаче аутентификации возбуждается [`AuthenticationError`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.AuthenticationError). См. [Authentication keys](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-auth-keys).18811882#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authkey]]]])`18831884Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.18851886*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.18871888> **Примечание**1889>1890> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.18911892*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них гарантированно доступна только первая. Если *family* равно `None`, то семейство определяется по формату *address*. Если *address* также равно `None`, выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [Форматы адресов](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равно `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/3/library/tempfile.html#tempfile.mkstemp).18931894Если объект listener использует сокет, то *backlog* (по умолчанию 1) передаётся методу [`listen()`](https://python-all.ru/3/library/socket.html#socket.socket.listen) сокета после его привязки.18951896Если *authkey* задан и не равен `None`, он должен быть байтовой строкой и будет использоваться как секретный ключ для аутентификации на основе HMAC. Если *authkey* равен `None`, аутентификация не выполняется. В случае неудачи аутентификации возбуждается [`AuthenticationError`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.AuthenticationError). См. [Ключи аутентификации](https://python-all.ru/3/library/multiprocessing.html#multiprocessing-auth-keys).18971898#### `accept()`18991900Принимает соединение на привязанном сокете или именованном канале объекта listener и возвращает объект [`Connection`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection). Если предпринята попытка аутентификации и она не удалась, возбуждается [`AuthenticationError`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.AuthenticationError).19011902#### `close()`19031904Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.19051906Объекты Listener имеют следующие свойства только для чтения:19071908#### `address`19091910Адрес, используемый объектом Listener.19111912#### `last_accepted`19131914Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то равен `None`.19151916Изменено в версии 3.3: Объекты Listener теперь поддерживают протокол управления контекстом – см. [Типы менеджеров контекста](https://python-all.ru/3/library/stdtypes.html#typecontextmanager). [`__enter__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__enter__) возвращает объект listener, а [`__exit__()`](https://python-all.ru/3/library/stdtypes.html#contextmanager.__exit__) вызывает [`close()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Listener.close).19171918#### `multiprocessing.connection.wait(object_list, timeout=None)`19191920Ожидает, пока объект из *object\_list* не будет готов. Возвращает список тех объектов из *object\_list*, которые готовы. Если *timeout* – число с плавающей точкой, вызов блокируется не более чем на указанное количество секунд. Если *timeout* равен `None`, блокировка будет неограниченной. Отрицательный тайм-аут эквивалентен нулевому.19211922На обеих платформах, POSIX и Windows, объект может находиться в *object\_list*, если он19231924- читаемый объект [`Connection`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection);1925- подключенный и читаемый объект [`socket.socket`](https://python-all.ru/3/library/socket.html#socket.socket); или1926- атрибут [`sentinel`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.sentinel) объекта [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process).19271928Объект соединения или сокета готов, когда из него доступны для чтения данные или другой конец был закрыт.19291930**POSIX**: `wait(object_list, timeout)` почти эквивалентен `select.select(object_list, [], [], timeout)`. Разница в том, что если [`select.select()`](https://python-all.ru/3/library/select.html#select.select) прерывается сигналом, он может возбудить [`OSError`](https://python-all.ru/3/library/exceptions.html#OSError) с кодом ошибки `EINTR`, тогда как `wait()` этого не сделает.19311932**Windows**: Элемент в *object\_list* должен быть либо целочисленным дескриптором, который ожидаем (согласно определению из документации функции Win32 `WaitForMultipleObjects()`), либо объектом с методом [`fileno()`](https://python-all.ru/3/library/io.html#io.IOBase.fileno), возвращающим дескриптор сокета или канала. (Обратите внимание: дескрипторы каналов и сокетов **не** являются ожидаемыми дескрипторами.)19331934Добавлено в версии 3.3.19351936**Примеры**19371938Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:19391940```python1941from multiprocessing.connection import Listener1942from array import array19431944address = ('localhost', 6000)     # семейство определяется как 'AF_INET'19451946with Listener(address, authkey=b'secret password') as listener:1947    with listener.accept() as conn:1948        print('connection accepted from', listener.last_accepted)19491950        conn.send([2.25, None, 'junk', float])19511952        conn.send_bytes(b'hello')19531954        conn.send_bytes(array('i', [42, 1729]))1955```19561957Следующий код подключается к серверу и получает от него некоторые данные:19581959```python1960from multiprocessing.connection import Client1961from array import array19621963address = ('localhost', 6000)19641965with Client(address, authkey=b'secret password') as conn:1966    print(conn.recv())                  # => [2.25, None, 'junk', float]19671968    print(conn.recv_bytes())            # => 'hello'19691970    arr = array('i', [0, 0, 0, 0, 0])1971    print(conn.recv_bytes_into(arr))    # => 81972    print(arr)                          # => array('i', [42, 1729, 0, 0, 0])1973```19741975Следующий код использует [`wait()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.wait) для ожидания сообщений от нескольких процессов одновременно:19761977```python1978from multiprocessing import Process, Pipe, current_process1979from multiprocessing.connection import wait19801981def foo(w):1982    for i in range(10):1983        w.send((i, current_process().name))1984    w.close()19851986if __name__ == '__main__':1987    readers = []19881989    for i in range(4):1990        r, w = Pipe(duplex=False)1991        readers.append(r)1992        p = Process(target=foo, args=(w,))1993        p.start()1994        # Закрываем записывающий конец канала, чтобы убедиться, что1995        # p – единственный процесс, владеющий дескриптором канала. Это1996        # гарантирует, что когда p закроет свой дескриптор для записывающего конца,1997        # wait() немедленно сообщит, что читающий конец готов1998        w.close()19992000    while readers:2001        for r in wait(readers):2002            try:2003                msg = r.recv()2004            except EOFError:2005                readers.remove(r)2006            else:2007                print(msg)2008```20092010#### Форматы адресов20112012- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.2013- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.2014- Адрес `'AF_PIPE'` – это строка вида `r'\\.\pipe\PipeName'`. Чтобы использовать [`Client()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Client) для подключения к именованному каналу на удалённом компьютере с именем *ServerName*, следует использовать адрес вида `r'\\ServerName\pipe\PipeName'`.20152016Обратите внимание: любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не `'AF_UNIX'`.20172018### Ключи аутентификации20192020При использовании [`Connection.recv`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Connection.recv) полученные данные автоматически распиковываются. К сожалению, распиковка данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/3/library/hmac.html#module-hmac) для обеспечения дайджест-аутентификации.20212022Ключ аутентификации – это байтовая строка, которую можно рассматривать как пароль: после установления соединения обе стороны требуют доказательства, что другая сторона знает ключ аутентификации. (Демонстрация того, что обе стороны используют один и тот же ключ, **не** предполагает отправку ключа по соединению.)20232024Если запрошена аутентификация, но ключ аутентификации не указан, то используется возвращаемое значение `current_process().authkey` (см. [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process)). Это значение автоматически наследуется любым объектом `Process`, который создаёт текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы используют один ключ аутентификации, который можно применять для настройки соединений между ними.20252026Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/3/library/os.html#os.urandom).20272028Эта аутентификация защищает соединения [`Listener`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.connection.Client), которые доступны по адресу. Она не применяется к анонимным каналам, созданным [`Pipe()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Pipe) или используемым внутри [`Queue`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue). `multiprocessing` считает все локальные процессы, работающие от того же пользователя, доверенными; в большинстве операционных систем такие процессы в любом случае могут обращаться к файловым дескрипторам каналов друг друга. Приложения, требующие изоляции между процессами одного пользователя, должны настраивать её на уровне операционной системы – например, запуская рабочие процессы под другой учётной записью или в песочнице.20292030### Журналирование20312032Доступна поддержка журналирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/3/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому сообщения от разных процессов могут перемешиваться (в зависимости от типа обработчика).20332034#### `multiprocessing.get_logger()`20352036Возвращает логгер, используемый модулем `multiprocessing`. При необходимости создаётся новый.20372038При первом создании логгер имеет уровень [`logging.NOTSET`](https://python-all.ru/3/library/logging.html#logging.NOTSET) и не имеет обработчика по умолчанию. Сообщения, отправленные этому логгеру, по умолчанию не распространяются на корневой логгер.20392040Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.20412042#### `multiprocessing.log_to_stderr(level=None)`20432044Эта функция выполняет вызов [`get_logger()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.get_logger), но в дополнение к возврату логгера, созданного функцией get\_logger, она добавляет обработчик, который отправляет вывод в [`sys.stderr`](https://python-all.ru/3/library/sys.html#sys.stderr), используя формат `'[%(levelname)s/%(processName)s] %(message)s'`. Можно изменить `levelname` логгера, передав аргумент `level`.20452046Ниже приведён пример сеанса с включённым журналированием:20472048```python2049>>> import multiprocessing, logging2050>>> logger = multiprocessing.log_to_stderr()2051>>> logger.setLevel(logging.INFO)2052>>> logger.warning('doomed')2053[WARNING/MainProcess] doomed2054>>> m = multiprocessing.Manager()2055[INFO/SyncManager-...] child process calling self.run()2056[INFO/SyncManager-...] created temp directory /.../pymp-...2057[INFO/SyncManager-...] manager serving at '/.../listener-...'2058>>> del m2059[INFO/MainProcess] sending shutdown message to manager2060[INFO/SyncManager-...] manager exiting with exitcode 02061```20622063Полную таблицу уровней журналирования см. в модуле [`logging`](https://python-all.ru/3/library/logging.html#module-logging).20642065### Модуль `multiprocessing.dummy`20662067`multiprocessing.dummy` воспроизводит API модуля `multiprocessing`, но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/3/library/threading.html#module-threading).20682069В частности, функция `Pool`, предоставляемая модулем `multiprocessing.dummy`, возвращает экземпляр [`ThreadPool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.ThreadPool), который является подклассом [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool), поддерживающим все те же вызовы методов, но использующим пул рабочих потоков вместо рабочих процессов.20702071#### `class multiprocessing.pool.ThreadPool([processes[, initializer[, initargs]]])`20722073Объект пула потоков, который управляет пулом рабочих потоков, которым можно отправлять задачи. Экземпляры `ThreadPool` полностью совместимы по интерфейсу с экземплярами [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool), и их ресурсами необходимо правильно управлять – либо используя пул как контекстный менеджер, либо вызывая [`close()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.close) и [`terminate()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool.terminate) вручную.20742075*processes* – это количество используемых рабочих потоков. Если *processes* равно `None`, то используется число, возвращаемое [`os.process_cpu_count()`](https://python-all.ru/3/library/os.html#os.process_cpu_count).20762077Если *initializer* не равно `None`, то каждый рабочий процесс вызовет `initializer(*initargs)` при запуске.20782079В отличие от [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool), *maxtasksperchild* и *context* не могут быть заданы.20802081> **Примечание**2082>2083> `ThreadPool` имеет тот же интерфейс, что и [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool), который разработан вокруг пула процессов и появился до введения модуля [`concurrent.futures`](https://python-all.ru/3/library/concurrent.futures.html#module-concurrent.futures). Таким образом, он наследует некоторые операции, которые не имеют смысла для пула на основе потоков, и имеет собственный тип для представления статуса асинхронных задач – [`AsyncResult`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.AsyncResult), который не распознаётся другими библиотеками.2084>2085> Пользователям обычно следует отдавать предпочтение использованию [`concurrent.futures.ThreadPoolExecutor`](https://python-all.ru/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor), который имеет более простой интерфейс, изначально разработанный для потоков, и возвращает экземпляры [`concurrent.futures.Future`](https://python-all.ru/3/library/concurrent.futures.html#concurrent.futures.Future), совместимые со многими другими библиотеками, включая [`asyncio`](https://python-all.ru/3/library/asyncio.html#module-asyncio).20862087## Рекомендации по программированию20882089Существуют определённые рекомендации и идиомы, которых следует придерживаться при использовании `multiprocessing`.20902091### Все методы запуска20922093Нижеследующее относится ко всем методам запуска.20942095Избегайте разделяемого состояния20962097> По возможности следует стараться избегать передачи больших объёмов данных между процессами.2098>2099> Вероятно, лучше всего ограничиться использованием очередей или каналов для связи между процессами, а не использовать низкоуровневые примитивы синхронизации.21002101Сериализуемость21022103> Убедитесь, что аргументы методов прокси-объектов сериализуемы.21042105Потокобезопасность прокси21062107> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.2108>2109> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)21102111Присоединение процессов-зомби21122113> В POSIX, когда процесс завершается, но не был присоединён, он становится зомби. Их никогда не должно быть много, потому что каждый раз при запуске нового процесса (или вызове [`active_children()`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.active_children)) все завершённые процессы, которые ещё не были присоединены, будут присоединены. Также вызов [`Process.is_alive`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.is_alive) у завершённого процесса присоединит его. Тем не менее, хорошей практикой является явное присоединение всех запущенных процессов.21142115Лучше наследовать, чем упаковывать/распаковывать21162117> При использовании методов запуска *spawn* или *forkserver* многие типы из `multiprocessing` должны быть упаковываемыми (picklable), чтобы дочерние процессы могли их использовать. Однако обычно следует избегать отправки разделяемых объектов другим процессам через каналы или очереди. Вместо этого стоит организовать программу так, чтобы процесс, которому требуется доступ к разделяемому ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.21182119Избегайте завершения процессов21202121> Использование метода [`Process.terminate`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.terminate) для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), которые в данный момент используются процессом, станут повреждёнными или недоступными для других процессов.2122>2123> Поэтому, вероятно, лучше рассматривать использование [`Process.terminate`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.terminate) только для процессов, которые никогда не используют разделяемые ресурсы.21242125Присоединение процессов, использующих очереди21262127> Имейте в виду, что процесс, поместивший элементы в очередь, будет ждать перед завершением, пока все буферизованные элементы не будут переданы потоком «feeder» в нижележащий канал. (Дочерний процесс может вызвать метод [`Queue.cancel_join_thread`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread) очереди, чтобы избежать такого поведения.)2128>2129> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещённые в очередь, будут в конечном итоге удалены до того, как процесс будет присоединён. В противном случае нельзя быть уверенным, что процессы, поместившие элементы в очередь, завершатся. Также помните, что недемонические процессы присоединяются автоматически.2130>2131> Пример, который приведёт к взаимоблокировке:2132>2133> ```python2134> from multiprocessing import Process, Queue2135>2136> def f(q):2137>     q.put('X' * 1000000)2138>2139> if __name__ == '__main__':2140>     queue = Queue()2141>     p = Process(target=f, args=(queue,))2142>     p.start()2143>     p.join()                    # это приводит к взаимоблокировке2144>     obj = queue.get()2145> ```2146>2147> Исправление заключается в замене двух последних строк местами (или просто удалении строки с `p.join()`).21482149Явная передача ресурсов дочерним процессам21502151> В POSIX при использовании метода запуска *fork* дочерний процесс может использовать разделяемый ресурс, созданный в родительском процессе, через глобальный ресурс. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.2152>2153> Помимо того, что это делает код (потенциально) совместимым с Windows и другими методами запуска, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если некоторый ресурс освобождается при сборке мусора объекта в родительском процессе.2154>2155> Так, например2156>2157> ```python2158> from multiprocessing import Process, Lock2159>2160> def f():2161>     ... do something using "lock" ...2162>2163> if __name__ == '__main__':2164>     lock = Lock()2165>     for i in range(10):2166>         Process(target=f).start()2167> ```2168>2169> следует переписать как2170>2171> ```python2172> from multiprocessing import Process, Lock2173>2174> def f(l):2175>     ... do something using "l" ...2176>2177> if __name__ == '__main__':2178>     lock = Lock()2179>     for i in range(10):2180>         Process(target=f, args=(lock,)).start()2181> ```21822183Остерегайтесь замены [`sys.stdin`](https://python-all.ru/3/library/sys.html#sys.stdin) на «файлоподобный объект»21842185> `multiprocessing` изначально безусловно вызывал:2186>2187> ```python2188> os.close(sys.stdin.fileno())2189> ```2190>2191> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:2192>2193> ```python2194> sys.stdin.close()2195> sys.stdin = open(os.open(os.devnull, os.O_RDONLY), closefd=False)2196> ```2197>2198> Это решает основную проблему столкновения процессов друг с другом, приводящую к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, которые заменяют [`sys.stdin()`](https://python-all.ru/3/library/sys.html#sys.stdin) на «файлоподобный объект» с буферизацией вывода. Опасность в том, что если несколько процессов вызовут [`close()`](https://python-all.ru/3/library/io.html#io.IOBase.close) для этого файлоподобного объекта, одни и те же данные могут быть сброшены в объект несколько раз, что приведёт к повреждению.2199>2200> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:2201>2202> ```python2203> @property2204> def cache(self):2205>     pid = os.getpid()2206>     if pid != self._pid:2207>         self._pid = pid2208>         self._cache = []2209>     return self._cache2210> ```2211>2212> Для получения дополнительной информации см. [bpo-5155](https://python-all.ru/3/library/multiprocessing.html), [bpo-5313](https://python-all.ru/3/library/multiprocessing.html) и [bpo-5331](https://python-all.ru/3/library/multiprocessing.html)22132214### Методы запуска *spawn* и *forkserver*22152216Существует несколько дополнительных ограничений, которые не относятся к методу запуска *fork*.22172218Упаковываемость22192220> Убедитесь, что все аргументы [`Process`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process) являются упаковываемыми (picklable). Кроме того, если вы создаёте подкласс `Process.__init__`, необходимо гарантировать, что экземпляры будут упаковываемы при вызове метода [`Process.start`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.start).22212222Глобальные переменные22232224> Имейте в виду, что если код, выполняемый в дочернем процессе, попытается обратиться к глобальной переменной, то значение, которое он увидит (если оно есть), может не совпадать со значением в родительском процессе на момент вызова [`Process.start`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.Process.start).2225>2226> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.22272228Безопасный импорт главного модуля22292230> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без нежелательных побочных эффектов (например, запуска нового процесса).2231>2232> Например, при использовании методов запуска *spawn* или *forkserver* выполнение следующего модуля приведёт к ошибке [`RuntimeError`](https://python-all.ru/3/library/exceptions.html#RuntimeError):2233>2234> ```python2235> from multiprocessing import Process2236>2237> def foo():2238>     print('hello')2239>2240> p = Process(target=foo)2241> p.start()2242> ```2243>2244> Вместо этого следует защитить «точку входа» программы с помощью `if __name__ == '__main__':` следующим образом:2245>2246> ```python2247> from multiprocessing import Process, freeze_support, set_start_method2248>2249> def foo():2250>     print('hello')2251>2252> if __name__ == '__main__':2253>     freeze_support()2254>     set_start_method('spawn')2255>     p = Process(target=foo)2256>     p.start()2257> ```2258>2259> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не как замороженное приложение.)2260>2261> Это позволяет только что порождённому интерпретатору Python безопасно импортировать модуль и затем выполнить функцию `foo()` этого модуля.2262>2263> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.22642265## Примеры22662267Демонстрация создания и использования настраиваемых менеджеров и прокси:22682269```python2270from multiprocessing import freeze_support2271from multiprocessing.managers import BaseManager, BaseProxy2272import operator22732274##22752276class Foo:2277    def f(self):2278        print('you called Foo.f()')2279    def g(self):2280        print('you called Foo.g()')2281    def _h(self):2282        print('you called Foo._h()')22832284# Простая функция-генератор2285def baz():2286    for i in range(10):2287        yield i*i22882289# Тип прокси для объектов-генераторов2290class GeneratorProxy(BaseProxy):2291    _exposed_ = ['__next__']2292    def __iter__(self):2293        return self2294    def __next__(self):2295        return self._callmethod('__next__')22962297# Функция для возврата модуля operator2298def get_operator_module():2299    return operator23002301##23022303class MyManager(BaseManager):2304    pass23052306# зарегистрировать класс Foo; сделать `f()` и `g()` доступными через прокси2307MyManager.register('Foo1', Foo)23082309# зарегистрировать класс Foo; сделать `g()` и `_h()` доступными через прокси2310MyManager.register('Foo2', Foo, exposed=('g', '_h'))23112312# зарегистрировать функцию-генератор baz; использовать `GeneratorProxy` для создания прокси2313MyManager.register('baz', baz, proxytype=GeneratorProxy)23142315# зарегистрировать get_operator_module(); сделать публичные функции доступными через прокси2316MyManager.register('operator', get_operator_module)23172318##23192320def test():2321    manager = MyManager()2322    manager.start()23232324    print('-' * 20)23252326    f1 = manager.Foo1()2327    f1.f()2328    f1.g()2329    assert not hasattr(f1, '_h')2330    assert sorted(f1._exposed_) == sorted(['f', 'g'])23312332    print('-' * 20)23332334    f2 = manager.Foo2()2335    f2.g()2336    f2._h()2337    assert not hasattr(f2, 'f')2338    assert sorted(f2._exposed_) == sorted(['g', '_h'])23392340    print('-' * 20)23412342    it = manager.baz()2343    for i in it:2344        print('<%d>' % i, end=' ')2345    print()23462347    print('-' * 20)23482349    op = manager.operator()2350    print('op.add(23, 45) =', op.add(23, 45))2351    print('op.pow(2, 94) =', op.pow(2, 94))2352    print('op._exposed_ =', op._exposed_)23532354##23552356if __name__ == '__main__':2357    freeze_support()2358    test()2359```23602361Использование [`Pool`](https://python-all.ru/3/library/multiprocessing.html#multiprocessing.pool.Pool):23622363```python2364import multiprocessing2365import time2366import random2367import sys23682369#2370# Функции, используемые тестовым кодом2371#23722373def calculate(func, args):2374    result = func(*args)2375    return '%s says that %s%s = %s' % (2376        multiprocessing.current_process().name,2377        func.__name__, args, result2378        )23792380def calculatestar(args):2381    return calculate(*args)23822383def mul(a, b):2384    time.sleep(0.5 * random.random())2385    return a * b23862387def plus(a, b):2388    time.sleep(0.5 * random.random())2389    return a + b23902391def f(x):2392    return 1.0 / (x - 5.0)23932394def pow3(x):2395    return x ** 323962397def noop(x):2398    pass23992400#2401# Тестовый код2402#24032404def test():2405    PROCESSES = 42406    print('Creating pool with %d processes\n' % PROCESSES)24072408    with multiprocessing.Pool(PROCESSES) as pool:2409        #2410        # Тесты2411        #24122413        TASKS = [(mul, (i, 7)) for i in range(10)] + \2414                [(plus, (i, 8)) for i in range(10)]24152416        results = [pool.apply_async(calculate, t) for t in TASKS]2417        imap_it = pool.imap(calculatestar, TASKS)2418        imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)24192420        print('Ordered results using pool.apply_async():')2421        for r in results:2422            print('\t', r.get())2423        print()24242425        print('Ordered results using pool.imap():')2426        for x in imap_it:2427            print('\t', x)2428        print()24292430        print('Unordered results using pool.imap_unordered():')2431        for x in imap_unordered_it:2432            print('\t', x)2433        print()24342435        print('Ordered results using pool.map() --- will block till complete:')2436        for x in pool.map(calculatestar, TASKS):2437            print('\t', x)2438        print()24392440        #2441        # Тестирование обработки ошибок2442        #24432444        print('Testing error handling:')24452446        try:2447            print(pool.apply(f, (5,)))2448        except ZeroDivisionError:2449            print('\tGot ZeroDivisionError as expected from pool.apply()')2450        else:2451            raise AssertionError('expected ZeroDivisionError')24522453        try:2454            print(pool.map(f, list(range(10))))2455        except ZeroDivisionError:2456            print('\tGot ZeroDivisionError as expected from pool.map()')2457        else:2458            raise AssertionError('expected ZeroDivisionError')24592460        try:2461            print(list(pool.imap(f, list(range(10)))))2462        except ZeroDivisionError:2463            print('\tGot ZeroDivisionError as expected from list(pool.imap())')2464        else:2465            raise AssertionError('expected ZeroDivisionError')24662467        it = pool.imap(f, list(range(10)))2468        for i in range(10):2469            try:2470                x = next(it)2471            except ZeroDivisionError:2472                if i == 5:2473                    pass2474            except StopIteration:2475                break2476            else:2477                if i == 5:2478                    raise AssertionError('expected ZeroDivisionError')24792480        assert i == 92481        print('\tGot ZeroDivisionError as expected from IMapIterator.next()')2482        print()24832484        #2485        # Тестирование тайм-аутов2486        #24872488        print('Testing ApplyResult.get() with timeout:', end=' ')2489        res = pool.apply_async(calculate, TASKS[0])2490        while 1:2491            sys.stdout.flush()2492            try:2493                sys.stdout.write('\n\t%s' % res.get(0.02))2494                break2495            except multiprocessing.TimeoutError:2496                sys.stdout.write('.')2497        print()2498        print()24992500        print('Testing IMapIterator.next() with timeout:', end=' ')2501        it = pool.imap(calculatestar, TASKS)2502        while 1:2503            sys.stdout.flush()2504            try:2505                sys.stdout.write('\n\t%s' % it.next(0.02))2506            except StopIteration:2507                break2508            except multiprocessing.TimeoutError:2509                sys.stdout.write('.')2510        print()2511        print()25122513if __name__ == '__main__':2514    multiprocessing.freeze_support()2515    test()2516```25172518Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:25192520```python2521import time2522import random25232524from multiprocessing import Process, Queue, current_process, freeze_support25252526#2527# Функция, выполняемая рабочими процессами2528#25292530def worker(input, output):2531    for func, args in iter(input.get, 'STOP'):2532        result = calculate(func, args)2533        output.put(result)25342535#2536# Функция, используемая для вычисления результата2537#25382539def calculate(func, args):2540    result = func(*args)2541    return '%s says that %s%s = %s' % \2542        (current_process().name, func.__name__, args, result)25432544#2545# Функции, на которые ссылаются задачи2546#25472548def mul(a, b):2549    time.sleep(0.5*random.random())2550    return a * b25512552def plus(a, b):2553    time.sleep(0.5*random.random())2554    return a + b25552556#2557#2558#25592560def test():2561    NUMBER_OF_PROCESSES = 42562    TASKS1 = [(mul, (i, 7)) for i in range(20)]2563    TASKS2 = [(plus, (i, 8)) for i in range(10)]25642565    # Создание очередей2566    task_queue = Queue()2567    done_queue = Queue()25682569    # Отправка задач2570    for task in TASKS1:2571        task_queue.put(task)25722573    # Запуск рабочих процессов2574    for i in range(NUMBER_OF_PROCESSES):2575        Process(target=worker, args=(task_queue, done_queue)).start()25762577    # Получение и вывод результатов2578    print('Unordered results:')2579    for i in range(len(TASKS1)):2580        print('\t', done_queue.get())25812582    # Добавить ещё задач с помощью `put()`2583    for task in TASKS2:2584        task_queue.put(task)25852586    # Получить и вывести ещё несколько результатов2587    for i in range(len(TASKS2)):2588        print('\t', done_queue.get())25892590    # Сообщить дочерним процессам об остановке2591    for i in range(NUMBER_OF_PROCESSES):2592        task_queue.put('STOP')25932594if __name__ == '__main__':2595    freeze_support()2596    test()2597```2598