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

multiprocessing.md

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

1> **Источник:** https://python-all.ru/2.7/library/multiprocessing.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 16.6. [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) – интерфейс «threading» на основе процессов89Новое в версии 2.6.1011## 16.6.1. Введение1213[`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) – это пакет, который поддерживает порождение процессов с помощью API, похожего на модуль [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). Пакет [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) обеспечивает как локальный, так и удалённый параллелизм, эффективно обходя [Глобальную блокировку интерпретатора](https://python-all.ru/2.7/glossary.html#term-global-interpreter-lock), используя подпроцессы вместо потоков. Благодаря этому модуль [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) позволяет программисту полностью использовать несколько процессоров на данной машине. Он работает как на Unix, так и на Windows.1415Модуль [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) также предоставляет API, не имеющие аналогов в модуле [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). Ярким примером является объект `Pool`, который предоставляет удобный способ распараллеливания выполнения функции для нескольких входных значений, распределяя входные данные между процессами (распараллеливание данных). В следующем примере демонстрируется распространённая практика определения таких функций в модуле, чтобы дочерние процессы могли успешно импортировать этот модуль. Этот базовый пример распараллеливания данных с использованием `Pool`,1617```python18from multiprocessing import Pool1920def f(x):21    return x*x2223if __name__ == '__main__':24    p = Pool(5)25    print(p.map(f, [1, 2, 3]))26```2728будет выводить на стандартный вывод2930```python31[1, 4, 9]32```3334### 16.6.1.1. Класс [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process)3536В [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) процессы порождаются созданием объекта [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) и последующим вызовом его метода [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start). [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) следует API [`threading.Thread`](https://python-all.ru/2.7/library/threading.html#threading.Thread). Простой пример многопроцессной программы:3738```python39from multiprocessing import Process4041def f(name):42    print 'hello', name4344if __name__ == '__main__':45    p = Process(target=f, args=('bob',))46    p.start()47    p.join()48```4950Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:5152```python53from multiprocessing import Process54import os5556def info(title):57    print title58    print 'module name:', __name__59    if hasattr(os, 'getppid'):  # доступно только на Unix60        print 'parent process:', os.getppid()61    print 'process id:', os.getpid()6263def f(name):64    info('function f')65    print 'hello', name6667if __name__ == '__main__':68    info('main line')69    p = Process(target=f, args=('bob',))70    p.start()71    p.join()72```7374Объяснение того, почему (в Windows) часть `if __name__ == '__main__'` необходима, см. в [рекомендациях по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming).7576### 16.6.1.2. Обмен объектами между процессами7778[`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) поддерживает два типа каналов связи между процессами:7980**Очереди**8182> Класс [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue) является почти точной копией [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue). Например:83>84> ```python85> from multiprocessing import Process, Queue86>87> def f(q):88>     q.put([42, None, 'hello'])89>90> if __name__ == '__main__':91>     q = Queue()92>     p = Process(target=f, args=(q,))93>     p.start()94>     print q.get()    # печатает "[42, None, 'hello']"95>     p.join()96> ```97>98> Очереди потокобезопасны и безопасны для процессов.99100**Каналы**101102> Функция [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является дуплексным (двунаправленным). Например:103>104> ```python105> from multiprocessing import Process, Pipe106>107> def f(conn):108>     conn.send([42, None, 'hello'])109>     conn.close()110>111> if __name__ == '__main__':112>     parent_conn, child_conn = Pipe()113>     p = Process(target=f, args=(child_conn,))114>     p.start()115>     print parent_conn.recv()   # печатает "[42, None, 'hello']"116>     p.join()117> ```118>119> Два объекта соединения, возвращаемые [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe), представляют два конца канала. Каждый объект соединения имеет методы [`send()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.send) и [`recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv) (среди прочих). Обратите внимание: данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.120121### 16.6.1.3. Синхронизация между процессами122123[`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) содержит эквиваленты всех примитивов синхронизации из [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс одновременно выводит на стандартный вывод:124125```python126from multiprocessing import Process, Lock127128def f(l, i):129    l.acquire()130    print 'hello world', i131    l.release()132133if __name__ == '__main__':134    lock = Lock()135136    for num in range(10):137        Process(target=f, args=(lock, num)).start()138```139140Без использования блокировки вывод от разных процессов может полностью перепутаться.141142### 16.6.1.4. Обмен состоянием между процессами143144Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.145146Однако если действительно необходимо использовать некоторые общие данные, то [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) предоставляет несколько способов сделать это.147148**Разделяемая память**149150> Данные могут храниться в отображении разделяемой памяти с помощью [`Value`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Array). Например, следующий код151>152> ```python153> from multiprocessing import Process, Value, Array154>155> def f(n, a):156>     n.value = 3.1415927157>     for i in range(len(a)):158>         a[i] = -a[i]159>160> if __name__ == '__main__':161>     num = Value('d', 0.0)162>     arr = Array('i', range(10))163>164>     p = Process(target=f, args=(num, arr))165>     p.start()166>     p.join()167>168>     print num.value169>     print arr[:]170> ```171>172> выведет173>174> ```python175> 3.1415927176> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]177> ```178>179> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, аналогичные используемым модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array): `'d'` означает число с плавающей запятой двойной точности, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасными для процессов и потоков.180>181> Для большей гибкости при работе с разделяемой памятью можно использовать модуль [`multiprocessing.sharedctypes`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.sharedctypes), который поддерживает создание произвольных объектов ctypes, выделенных из разделяемой памяти.182183**Серверный процесс**184185> Объект-менеджер, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими с помощью прокси.186>187> Менеджер, возвращаемый `Manager()`, будет поддерживать типы `list`, [`dict`](https://python-all.ru/2.7/library/stdtypes.html#dict), [`Namespace`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.Namespace), [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), [`Semaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Semaphore), [`BoundedSemaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Condition`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Condition), [`Event`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Event), [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), [`Value`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Value) и [`Array`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Array). Например,188>189> ```python190> from multiprocessing import Process, Manager191>192> def f(d, l):193>     d[1] = '1'194>     d['2'] = 2195>     d[0.25] = None196>     l.reverse()197>198> if __name__ == '__main__':199>     manager = Manager()200>201>     d = manager.dict()202>     l = manager.list(range(10))203>204>     p = Process(target=f, args=(d, l))205>     p.start()206>     p.join()207>208>     print d209>     print l210> ```211>212> выведет213>214> ```python215> {0.25: None, 1: '1', '2': 2}216> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]217> ```218>219> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.220221### 16.6.1.5. Использование пула процессов222223Класс `Pool` представляет пул рабочих процессов. Он имеет методы, которые позволяют передавать задачи рабочим процессам несколькими различными способами.224225Например:226227```python228from multiprocessing import Pool, TimeoutError229import time230import os231232def f(x):233    return x*x234235if __name__ == '__main__':236    pool = Pool(processes=4)              # запустить 4 рабочих процесса237238    # печатает "[0, 1, 4,..., 81]"239    print pool.map(f, range(10))240241    # печатает те же числа в произвольном порядке242    for i in pool.imap_unordered(f, range(10)):243        print i244245    # вычисляет "f(20)" асинхронно246    res = pool.apply_async(f, (20,))      # выполняется *только* в одном процессе247    print res.get(timeout=1)              # печатает "400"248249    # вычисляет "os.getpid()" асинхронно250    res = pool.apply_async(os.getpid, ()) # выполняется *только* в одном процессе251    print res.get(timeout=1)              # печатает PID этого процесса252253    # запуск нескольких асинхронных вычислений *может* задействовать больше процессов254    multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]255    print [res.get(timeout=1) for res in multiple_results]256257    # заставить один рабочий процесс спать 10 секунд258    res = pool.apply_async(time.sleep, (10,))259    try:260        print res.get(timeout=1)261    except TimeoutError:262        print "We lacked patience and got a multiprocessing.TimeoutError"263```264265Обратите внимание: методы пула должны использоваться только тем процессом, который его создал.266267> **Примечание**268>269> Для работы функциональности этого пакета требуется, чтобы модуль `__main__` был доступен для импорта дочерними процессами. Это рассматривается в [рекомендациях по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming), но здесь стоит упомянуть об этом. Это означает, что некоторые примеры, такие как примеры с `Pool`, не будут работать в интерактивном интерпретаторе. Например:270>271> ```python272> >>> from multiprocessing import Pool273> >>> p = Pool(5)274> >>> def f(x):275> ...     return x*x276> ...277> >>> p.map(f, [1,2,3])278> Process PoolWorker-1:279> Process PoolWorker-2:280> Process PoolWorker-3:281> Traceback (most recent call last):282> AttributeError: 'module' object has no attribute 'f'283> AttributeError: 'module' object has no attribute 'f'284> AttributeError: 'module' object has no attribute 'f'285> ```286>287> (Если попробовать это сделать, на самом деле будут выведены три полные трассировки стека, перемешанные в полу-случайном порядке, и затем, возможно, придётся как-то остановить главный процесс.)288289## 16.6.2. Справочник290291Пакет [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) в основном повторяет API модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).292293### 16.6.2.1. [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) и исключения294295#### `class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})`296297Объекты Process представляют активность, выполняемую в отдельном процессе. Класс [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) имеет эквиваленты всех методов [`threading.Thread`](https://python-all.ru/2.7/library/threading.html#threading.Thread).298299Конструктор всегда следует вызывать с именованными аргументами. *group* всегда должен быть `None`; он существует только для совместимости с [`threading.Thread`](https://python-all.ru/2.7/library/threading.html#threading.Thread). *target* – это вызываемый объект, который будет вызван методом [`run()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.run). По умолчанию он равен `None`, то есть ничего не вызывается. *name* – это имя процесса. По умолчанию создаётся уникальное имя вида 'Process-N1:N2:…:Nk', где N1,N2,…,Nk – последовательность целых чисел, длина которой определяется *поколением* процесса. *args* – это кортеж аргументов для вызова цели. *kwargs* – это словарь именованных аргументов для вызова цели. По умолчанию никакие аргументы не передаются в *target*.300301Если подкласс переопределяет конструктор, он должен вызывать конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.302303#### `run()`304305Метод, представляющий активность процесса.306307Этот метод можно переопределить в подклассе. Стандартный метод [`run()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.308309#### `start()`310311Запускает активность процесса.312313Этот метод должен быть вызван не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.314315#### `join([timeout])`316317Блокирует вызывающий поток до завершения процесса, чей метод [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.join) был вызван, или до истечения указанного таймаута.318319Если *timeout* равен `None`, таймаут отсутствует.320321Процесс может быть присоединён много раз.322323Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.324325#### `name`326327Имя процесса.328329Имя – это строка, используемая только для идентификации. Она не несет семантической нагрузки. Несколько процессов могут иметь одинаковое имя. Начальное имя задается конструктором.330331#### `is_alive()`332333Возвращает, жив ли процесс.334335Грубо говоря, объект процесса живёт с момента возврата метода [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.336337#### `daemon`338339Флаг демона процесса (логическое значение). Должен быть установлен до вызова [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start).340341Начальное значение наследуется от создающего процесса.342343Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.344345Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.346347В дополнение к API [`threading.Thread`](https://python-all.ru/2.7/library/threading.html#threading.Thread) объекты [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) также поддерживают следующие атрибуты и методы:348349#### `pid`350351Возвращает идентификатор процесса. До запуска процесса это будет `None`.352353#### `exitcode`354355Код завершения дочернего процесса. Он будет равен `None`, если процесс ещё не завершён. Отрицательное значение *-N* означает, что дочерний процесс был завершён сигналом *N*.356357#### `authkey`358359Ключ аутентификации процесса (байтовая строка).360361При инициализации [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) главному процессу назначается случайная строка с помощью [`os.urandom()`](https://python-all.ru/2.7/library/os.html#os.urandom).362363При создании объекта [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) он наследует ключ аутентификации своего родительского процесса, хотя его можно изменить, установив [`authkey`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.authkey) в другую байтовую строку.364365См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).366367#### `terminate()`368369Завершает процесс. На Unix это делается с помощью сигнала `SIGTERM`; на Windows используется `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.д. выполняться не будут.370371Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.372373> **Предупреждение**374>375> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.376377Обратите внимание: методы [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start), [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.join), [`is_alive()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.is_alive), [`terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) и [`exitcode`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.exitcode) должны вызываться только тем процессом, который создал объект процесса.378379Пример использования некоторых методов [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process):380381```pycon382>>> import multiprocessing, time, signal383>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))384>>> print p, p.is_alive()385<Process(Process-1, initial)> False386>>> p.start()387>>> print p, p.is_alive()388<Process(Process-1, started)> True389>>> p.terminate()390>>> time.sleep(0.1)391>>> print p, p.is_alive()392<Process(Process-1, stopped[SIGTERM])> False393>>> p.exitcode == -signal.SIGTERM394True395```396397#### `exception multiprocessing.BufferTooShort`398399Исключение, возбуждаемое [`Connection.recv_bytes_into()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv_bytes_into), когда предоставленный буфер слишком мал для чтения сообщения.400401Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде байтовой строки.402403### 16.6.2.2. Каналы и очереди404405При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.406407Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает несколько производителей и потребителей).408409Типы [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), [`multiprocessing.queues.SimpleQueue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.multiprocessing.queues.SimpleQueue) и [`JoinableQueue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue) – это очереди FIFO с несколькими производителями и потребителями, основанные на классе [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue) из стандартной библиотеки. Они отличаются тем, что в [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue) отсутствуют методы [`task_done()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.task_done) и [`join()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.join), появившиеся в классе [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue) в Python 2.5.410411Если используется [`JoinableQueue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue), то **обязательно** вызывайте [`JoinableQueue.task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) для каждой задачи, извлечённой из очереди; в противном случае семафор, используемый для подсчёта незавершённых задач, может переполниться, что приведёт к возбуждению исключения.412413Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [Менеджеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).414415> **Примечание**416>417> [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) использует обычные исключения [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty) и [`Queue.Full`](https://python-all.ru/2.7/library/queue.html#Queue.Full) для сигнализации тайм-аута. Они недоступны в пространстве имён [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing), поэтому их необходимо импортировать из [`Queue`](https://python-all.ru/2.7/library/queue.html#module-Queue).418419> **Примечание**420>421> Когда объект помещается в очередь, он сериализуется с помощью модуля pickle, и фоновый поток позднее сбрасывает сериализованные данные в нижележащий канал. Это имеет некоторые неожиданные последствия, но обычно не вызывает практических трудностей – если это действительно беспокоит, можно вместо этого использовать очередь, созданную с помощью [менеджера](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).422>423> 1. После помещения объекта в пустую очередь может пройти бесконечно малая задержка, прежде чем метод [`empty()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.empty) очереди вернёт [`False`](https://python-all.ru/2.7/library/constants.html#False) и [`get_nowait()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.get_nowait) сможет вернуться без возбуждения [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty).424> 2. Если несколько процессов помещают объекты в очередь, возможно получение объектов на другом конце не по порядку. Однако объекты, помещённые в очередь одним и тем же процессом, всегда будут идти друг за другом в ожидаемом порядке.425426> **Предупреждение**427>428> Если процесс завершается принудительно с помощью [`Process.terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) или [`os.kill()`](https://python-all.ru/2.7/library/os.html#os.kill) в то время, как он пытается использовать [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), данные в очереди могут быть повреждены. Это может привести к тому, что любой другой процесс при попытке использовать очередь получит исключение.429430> **Предупреждение**431>432> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не использовал [`JoinableQueue.cancel_join_thread`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread)), он не завершится, пока все буферизованные элементы не будут сброшены в канал.433>434> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.435>436> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [Рекомендации по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming).437438Пример использования очередей для межпроцессного взаимодействия см. в разделе [Примеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-examples).439440#### `multiprocessing.Pipe([duplex])`441442Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection), представляющих концы канала.443444Если *duplex* равно `True` (по умолчанию), то канал двунаправленный. Если *duplex* равно `False`, то канал однонаправленный: `conn1` можно использовать только для получения сообщений, а `conn2` – только для отправки.445446#### `class multiprocessing.Queue([maxsize])`447448Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.449450Обычные исключения [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty) и [`Queue.Full`](https://python-all.ru/2.7/library/queue.html#Queue.Full) из модуля [`Queue`](https://python-all.ru/2.7/library/queue.html#module-Queue) стандартной библиотеки возбуждаются для сигнализации тайм-аутов.451452[`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue) реализует все методы [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue), за исключением [`task_done()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.task_done) и [`join()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.join).453454#### `qsize()`455456Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.457458Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.NotImplementedError) на Unix-платформах, таких как Mac OS X, где `sem_getvalue()` не реализован.459460#### `empty()`461462Возвращает `True`, если очередь пуста, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.463464#### `full()`465466Возвращает `True`, если очередь заполнена, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.467468#### `put(obj[, block[, timeout]])`469470Помещает obj в очередь. Если необязательный аргумент *block* равен `True` (по умолчанию) и *timeout* равен `None` (по умолчанию), при необходимости блокируется до появления свободного места. Если *timeout* положительное число, блокируется не более чем на *timeout* секунд и возбуждает исключение [`Queue.Full`](https://python-all.ru/2.7/library/queue.html#Queue.Full), если за это время свободное место не появилось. В противном случае (*block* равен `False`) помещает элемент в очередь, если свободное место доступно немедленно, иначе возбуждает исключение [`Queue.Full`](https://python-all.ru/2.7/library/queue.html#Queue.Full) (*timeout* в этом случае игнорируется).471472#### `put_nowait(obj)`473474Эквивалентно `put(obj, False)`.475476#### `get([block[, timeout]])`477478Удаляет и возвращает элемент из очереди. Если необязательный аргумент *block* равен `True` (по умолчанию) и *timeout* равен `None` (по умолчанию), при необходимости блокируется до появления элемента. Если *timeout* положительное число, блокируется не более чем на *timeout* секунд и возбуждает исключение [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty), если за это время элемент не появился. В противном случае (block равен `False`) возвращает элемент, если он доступен немедленно, иначе возбуждает исключение [`Queue.Empty`](https://python-all.ru/2.7/library/queue.html#Queue.Empty) (*timeout* в этом случае игнорируется).479480#### `get_nowait()`481482Эквивалентно `get(False)`.483484[`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue) имеет несколько дополнительных методов, отсутствующих в [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue). Обычно эти методы не нужны в большинстве кода:485486#### `close()`487488Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.489490#### `join_thread()`491492Присоединяет фоновый поток. Этот метод можно использовать только после вызова [`close()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.close). Он блокируется до завершения фонового потока, гарантируя, что все данные из буфера сброшены в канал.493494По умолчанию, если процесс не является создателем очереди, при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`cancel_join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread), чтобы сделать [`join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.join_thread) бездействующим.495496#### `cancel_join_thread()`497498Предотвращает блокировку [`join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.join_thread). В частности, это предотвращает автоматическое присоединение фонового потока при завершении процесса – см. [`join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.join_thread).499500Более подходящим именем для этого метода могло бы быть `allow_exit_without_flush()`. Он, скорее всего, приведёт к потере поставленных в очередь данных, и почти наверняка вам не понадобится его использовать. Он нужен только в том случае, если текущий процесс должен немедленно завершиться, не дожидаясь сброса поставленных в очередь данных в нижележащий канал, и вас не волнует потеря данных.501502> **Примечание**503>504> Функциональность этого класса требует работающей реализации общего семафора в хост-операционной системе. Без неё функциональность класса будет отключена, а попытки создать экземпляр [`Queue`](https://python-all.ru/2.7/library/queue.html#module-Queue) приведут к [`ImportError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ImportError). См. [bpo-3770](https://python-all.ru/2.7/library/multiprocessing.html) для дополнительной информации. То же самое относится и ко всем специализированным типам очередей, перечисленным ниже.505506#### `class multiprocessing.queues.SimpleQueue`507508Это упрощённый тип [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), очень похожий на [`Pipe`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) с блокировкой.509510#### `empty()`511512Возвращает `True`, если очередь пуста, иначе `False`.513514#### `get()`515516Извлекает и возвращает элемент из очереди.517518#### `put(item)`519520Помещает *item* в очередь.521522#### `class multiprocessing.JoinableQueue([maxsize])`523524[`JoinableQueue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue), подкласс [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), – это очередь, которая дополнительно имеет методы [`task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) и [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.join).525526#### `task_done()`527528Указывает, что ранее поставленная в очередь задача завершена. Используется потоками-потребителями очереди. Для каждого вызова [`get()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.get), использованного для получения задачи, последующий вызов [`task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done) сообщает очереди, что обработка задачи завершена.529530Если [`join()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.join) в данный момент заблокирован, он возобновится, когда все элементы будут обработаны (то есть для каждого элемента, который был [`put()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.put) в очередь, был получен вызов [`task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done)).531532Вызывает [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError), если вызывается больше раз, чем было помещено элементов в очередь.533534#### `join()`535536Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.537538Счётчик незавершённых задач увеличивается при каждом добавлении элемента в очередь. Счётчик уменьшается, когда поток-потребитель вызывает [`task_done()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.JoinableQueue.task_done), чтобы указать, что элемент получен и вся работа с ним завершена. Когда счётчик незавершённых задач падает до нуля, [`join()`](https://python-all.ru/2.7/library/queue.html#Queue.Queue.join) разблокируется.539540### 16.6.2.3. Разное541542#### `multiprocessing.active_children()`543544Возвращает список всех активных дочерних процессов текущего процесса.545546Вызов этой функции имеет побочный эффект – «присоединение» всех уже завершённых процессов.547548#### `multiprocessing.cpu_count()`549550Возвращает количество процессоров в системе. Может вызывать [`NotImplementedError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.NotImplementedError).551552#### `multiprocessing.current_process()`553554Возвращает объект [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.555556Аналог [`threading.current_thread()`](https://python-all.ru/2.7/library/threading.html#threading.current_thread).557558#### `multiprocessing.freeze_support()`559560Добавляет поддержку для случая, когда программа, использующая [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing), была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)561562Эту функцию необходимо вызвать сразу после строки `if __name__ == '__main__'` в главном модуле. Например:563564```python565from multiprocessing import Process, freeze_support566567def f():568    print 'hello world!'569570if __name__ == '__main__':571    freeze_support()572    Process(target=f).start()573```574575Если строка `freeze_support()` опущена, то попытка запустить замороженный исполняемый файл вызовет [`RuntimeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.RuntimeError).576577Вызов `freeze_support()` не имеет эффекта при запуске на любой операционной системе, кроме Windows. Кроме того, если модуль запускается обычным образом интерпретатором Python в Windows (программа не была заморожена), то `freeze_support()` также не имеет эффекта.578579#### `multiprocessing.set_executable()`580581Устанавливает путь к интерпретатору Python, используемому при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/2.7/library/sys.html#sys.executable)). Встраивающим пользователям, вероятно, потребуется сделать что-то вроде582583```python584set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))585```586587прежде чем они смогут создавать дочерние процессы. (Только для Windows)588589> **Примечание**590>591> [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) не содержит аналогов [`threading.active_count()`](https://python-all.ru/2.7/library/threading.html#threading.active_count), [`threading.enumerate()`](https://python-all.ru/2.7/library/threading.html#threading.enumerate), [`threading.settrace()`](https://python-all.ru/2.7/library/threading.html#threading.settrace), [`threading.setprofile()`](https://python-all.ru/2.7/library/threading.html#threading.setprofile), [`threading.Timer`](https://python-all.ru/2.7/library/threading.html#threading.Timer) или [`threading.local`](https://python-all.ru/2.7/library/threading.html#threading.local).592593### 16.6.2.4. Объекты соединений594595Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.596597Объекты подключения обычно создаются с помощью [`Pipe`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) – см. также [Слушатели и клиенты](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-listeners-clients).598599#### `class Connection`600601#### `send(obj)`602603Отправляет объект на другой конец подключения; его следует читать с помощью [`recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv).604605Объект должен быть сериализуемым. Очень большие сериализованные объекты (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError).606607#### `recv()`608609Возвращает объект, отправленный с другого конца подключения с помощью [`send()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.send). Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего получать и другой конец был закрыт.610611#### `fileno()`612613Возвращает файловый дескриптор или дескриптор (handle), используемый подключением.614615#### `close()`616617Закрывает подключение.618619Вызывается автоматически при сборке мусора для подключения.620621#### `poll([timeout])`622623Возвращает, есть ли данные для чтения.624625Если *timeout* не указан, то метод возвращается немедленно. Если *timeout* – число, то оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, то используется бесконечное ожидание.626627#### `send_bytes(buffer[, offset[, size]])`628629Отправляет байтовые данные из объекта, поддерживающего интерфейс буфера, в виде целого сообщения.630631Если указан *offset*, то данные читаются с этой позиции в *buffer*. Если указан *size*, то из буфера будет прочитано соответствующее количество байт. Очень большие буферы (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError)632633#### `recv_bytes([maxlength])`634635Возвращает полное сообщение из байтовых данных, отправленное с другого конца подключения, в виде строки. Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего получать и другой конец закрыт.636637Если указан *maxlength* и сообщение длиннее *maxlength*, то вызывается [`IOError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.IOError), и подключение больше не будет доступно для чтения.638639#### `recv_bytes_into(buffer[, offset])`640641Читает в *buffer* полное сообщение с байтовыми данными, отправленное с другого конца соединения, и возвращает количество байтов в сообщении. Блокирует выполнение, пока не появится что-то для приёма. Возбуждает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего принимать и другой конец был закрыт.642643*buffer* должен быть объектом, удовлетворяющим интерфейсу буфера для записи. Если задан *offset*, сообщение будет записано в буфер, начиная с этой позиции. Значение offset должно быть неотрицательным целым числом, меньшим длины *buffer* (в байтах).644645Если буфер слишком мал, то вызывается исключение `BufferTooShort` и полное сообщение доступно как `e.args[0]`, где `e` экземпляр исключения.646647Например:648649```pycon650>>> from multiprocessing import Pipe651>>> a, b = Pipe()652>>> a.send([1, 'hello', None])653>>> b.recv()654[1, 'hello', None]655>>> b.send_bytes('thank you')656>>> a.recv_bytes()657'thank you'658>>> import array659>>> arr1 = array.array('i', range(5))660>>> arr2 = array.array('i', [0] * 10)661>>> a.send_bytes(arr1)662>>> count = b.recv_bytes_into(arr2)663>>> assert count == len(arr1) * arr1.itemsize664>>> arr2665array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])666```667668> **Предупреждение**669>670> Метод [`Connection.recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv) автоматически десериализует полученные данные, что может быть угрозой безопасности, если нельзя доверять процессу, отправившему сообщение.671>672> Поэтому, если объект подключения не был создан с помощью `Pipe()` следует использовать методы [`recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv) и [`send()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.send) только после выполнения некоторой аутентификации. См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).673674> **Предупреждение**675>676> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.677678### 16.6.2.5. Примитивы синхронизации679680В целом, в многопроцессных программах примитивы синхронизации требуются не так часто, как в многопоточных. См. документацию модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).681682Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [Менеджеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).683684#### `class multiprocessing.BoundedSemaphore([value])`685686Объект ограниченного семафора: близкий аналог [`threading.BoundedSemaphore`](https://python-all.ru/2.7/library/threading.html#threading.BoundedSemaphore).687688Единственное отличие от его близкого аналога: первый аргумент метода `acquire` называется *block*, и он поддерживает необязательный второй аргумент *timeout*, что согласуется с [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire).689690> **Примечание**691>692> На Mac OS X это неотличимо от [`Semaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализован на этой платформе.693694#### `class multiprocessing.Condition([lock])`695696Условная переменная: клон [`threading.Condition`](https://python-all.ru/2.7/library/threading.html#threading.Condition).697698Если указана *блокировка*, то она должна быть объектом [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) из [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing).699700#### `class multiprocessing.Event`701702Клон [`threading.Event`](https://python-all.ru/2.7/library/threading.html#threading.Event). Этот метод возвращает состояние внутреннего семафора при выходе, поэтому он всегда возвращает `True`, за исключением случаев, когда задан таймаут и операция истекает по таймауту.703704Изменено в версии 2.7: Ранее метод всегда возвращал `None`.705706#### `class multiprocessing.Lock`707708Нерекурсивный объект блокировки: близкий аналог [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock). Когда процесс или поток захватывает блокировку, последующие попытки захватить её из любого процесса или потока будут блокироваться до её освобождения; любой процесс или поток может её освободить. Концепции и поведение [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock) применительно к потокам воспроизводятся здесь в [`multiprocessing.Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) применительно к процессам или потокам, за исключением отмеченных случаев.709710Обратите внимание, что [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.Lock`, инициализированный контекстом по умолчанию.711712[`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) поддерживает протокол [менеджера контекста](https://python-all.ru/2.7/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with).713714#### `acquire(block=True, timeout=None)`715716Захватывает блокировку, блокирующую или неблокирующую.717718Если аргумент *block* установлен в `True` (по умолчанию), вызов метода будет блокироваться, пока блокировка не окажется в разблокированном состоянии, затем установит её в заблокированное состояние и вернёт `True`. Обратите внимание, что имя этого первого аргумента отличается от имени в [`threading.Lock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.Lock.acquire).719720Если аргумент *block* установлен в `False`, вызов метода не блокируется. Если блокировка в данный момент находится в заблокированном состоянии, возвращается `False`; в противном случае блокировка устанавливается в заблокированное состояние и возвращается `True`.721722При вызове с положительным значением с плавающей точкой для *timeout* блокируется не более чем на число секунд, указанное в *timeout*, пока блокировка не будет получена. Вызовы с отрицательным значением для *timeout* эквивалентны *timeout*, равному нулю. Вызовы с *timeout*, равным `None` (по умолчанию), устанавливают период ожидания бесконечным. Аргумент *timeout* не имеет практического значения, если аргумент *block* установлен в `False`, и поэтому игнорируется. Возвращает `True`, если блокировка получена, или `False`, если период ожидания истёк. Обратите внимание, что аргумент *timeout* не существует в аналоге этого метода, [`threading.Lock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.Lock.acquire).723724#### `release()`725726Освобождает блокировку. Может вызываться из любого процесса или потока, а не только из того, который изначально захватил блокировку.727728Поведение аналогично [`threading.Lock.release()`](https://python-all.ru/2.7/library/threading.html#threading.Lock.release) за исключением того, что при вызове на незаблокированной блокировке возбуждается [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError).729730#### `class multiprocessing.RLock`731732Объект рекурсивной блокировки: близкий аналог [`threading.RLock`](https://python-all.ru/2.7/library/threading.html#threading.RLock). Рекурсивная блокировка должна быть освобождена тем же процессом или потоком, который её захватил. После того как процесс или поток захватил рекурсивную блокировку, тот же процесс или поток может захватить её снова без блокировки; этот процесс или поток должен освободить её по одному разу за каждый захват.733734Обратите внимание, что [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.RLock`, инициализированный контекстом по умолчанию.735736[`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) поддерживает протокол [менеджера контекста](https://python-all.ru/2.7/glossary.html#term-context-manager) и поэтому может использоваться в операторах [`with`](https://python-all.ru/2.7/reference/compound_stmts.html#with).737738#### `acquire(block=True, timeout=None)`739740Захватывает блокировку, блокирующую или неблокирующую.741742При вызове с аргументом *block*, установленным в `True`, блокируется до тех пор, пока блокировка не окажется в разблокированном состоянии (не принадлежит ни одному процессу или потоку), если только блокировка уже не принадлежит текущему процессу или потоку. Затем текущий процесс или поток получает владение блокировкой (если ещё не владеет ею), и уровень рекурсии внутри блокировки увеличивается на единицу, возвращая значение `True`. Обратите внимание, что имеется несколько различий в поведении этого первого аргумента по сравнению с реализацией [`threading.RLock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.RLock.acquire), начиная с названия самого аргумента.743744При вызове с аргументом *block*, установленным в `False`, не блокируется. Если блокировка уже была захвачена (и, следовательно, принадлежит) другим процессом или потоком, текущий процесс или поток не получает владение, а уровень рекурсии внутри блокировки не изменяется, возвращая значение `False`. Если блокировка находится в разблокированном состоянии, текущий процесс или поток получает владение, а уровень рекурсии увеличивается, возвращая значение `True`.745746Использование и поведение аргумента *timeout* такие же, как в [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire). Обратите внимание, что аргумент *timeout* не существует в аналоге этого метода, [`threading.RLock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.RLock.acquire).747748#### `release()`749750Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения уровень рекурсии становится нулевым, переводит блокировку в разблокированное состояние (не принадлежит ни одному процессу или потоку), и если какие-либо другие процессы или потоки ожидают разблокировки, позволяет ровно одному из них продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся заблокированной и принадлежащей вызывающему процессу или потоку.751752Вызывайте этот метод только когда вызывающий процесс или поток владеет блокировкой. Исключение [`AssertionError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AssertionError) возбуждается, если этот метод вызывается процессом или потоком, не являющимся владельцем, или если блокировка находится в разблокированном (непринадлежащем) состоянии. Обратите внимание, что тип возбуждаемого исключения в этой ситуации отличается от реализованного поведения в [`threading.RLock.release()`](https://python-all.ru/2.7/library/threading.html#threading.RLock.release).753754#### `class multiprocessing.Semaphore([value])`755756Объект семафора: близкий аналог [`threading.Semaphore`](https://python-all.ru/2.7/library/threading.html#threading.Semaphore).757758Единственное отличие от его близкого аналога: первый аргумент его метода `acquire` называется *block*, и он поддерживает необязательный второй аргумент *timeout*, в соответствии с [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire).759760> **Примечание**761>762> The `acquire()` method of [`BoundedSemaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.BoundedSemaphore), [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock), [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) and [`Semaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Semaphore) has a timeout parameter not supported by the equivalents in [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). The signature is `acquire(block=True, timeout=None)` with keyword parameters being acceptable. If *block* is `True` and *timeout* is not `None` then it specifies a timeout in seconds. If *block* is `False` then *timeout* is ignored.763>764> На Mac OS X `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с тайм-аутом будет эмулировать поведение этой функции с помощью спящего цикла.765766> **Примечание**767>768> Если сигнал SIGINT, сгенерированный `Ctrl-C`, поступает, когда главный поток блокирован вызовом `BoundedSemaphore.acquire()`, [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire), [`RLock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock.acquire), `Semaphore.acquire()`, `Condition.acquire()` или `Condition.wait()`, то вызов будет немедленно прерван и возбуждено [`KeyboardInterrupt`](https://python-all.ru/2.7/library/exceptions.html#exceptions.KeyboardInterrupt).769>770> Это отличается от поведения [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading), где SIGINT игнорируется, пока выполняются эквивалентные блокирующие вызовы.771772> **Примечание**773>774> Некоторые функции этого пакета требуют работающей реализации общего семафора в операционной системе. При её отсутствии модуль `multiprocessing.synchronize` будет отключён, а попытки импортировать его приведут к [`ImportError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ImportError). См. [bpo-3770](https://python-all.ru/2.7/library/multiprocessing.html) для получения дополнительной информации.775776### 16.6.2.6. Разделяемые [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes) объекты777778Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.779780#### `multiprocessing.Value(typecode_or_type, *args[, lock])`781782Возвращает объект [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes), выделенный из общей памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для этого объекта.783784*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). *\*args* передаётся конструктору типа.785786Если *блокировка* равна `True` (по умолчанию), то создаётся новый рекурсивный объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".787788Операции, подобные `+=`, которые включают чтение и запись, не являются атомарными. Поэтому, если, например, требуется атомарно увеличить разделяемое значение, недостаточно просто сделать789790```python791counter.value += 1792```793794При условии, что связанная блокировка рекурсивна (по умолчанию она такой и является), можно вместо этого сделать795796```python797with counter.get_lock():798    counter.value += 1799```800801Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.802803#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`804805Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.806807*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо ctypes-тип, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.808809Если *блокировка* равен `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* – объект [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равен `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».810811Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.812813Обратите внимание, что массив типа [`ctypes.c_char`](https://python-all.ru/2.7/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.814815#### 16.6.2.6.1. Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.sharedctypes)816817Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.sharedctypes) предоставляет функции для выделения объектов [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.818819> **Примечание**820>821> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.822823#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`824825Возвращает массив ctypes, выделенный из разделяемой памяти.826827*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.828829Обратите внимание, что установка и получение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.830831#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`832833Возвращает объект ctypes, выделенный из разделяемой памяти.834835*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). *\*args* передаётся конструктору типа.836837Обратите внимание, что установка и получение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.838839Обратите внимание, что массив [`ctypes.c_char`](https://python-all.ru/2.7/library/ctypes.html#ctypes.c_char) имеет атрибуты `value` и `raw`, которые позволяют использовать его для сохранения и извлечения строк – см. документацию к [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes).840841#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *args[, lock])`842843То же, что и [`RawArray()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного массива ctypes.844845Если *блокировка* равна `True` (по умолчанию), то создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* является объектом [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), то она будет использоваться для синхронизации доступа к значению. Если *блокировка* равна `False`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".846847Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.848849#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args[, lock])`850851То же, что и [`RawValue()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного объекта ctypes.852853Если *блокировка* равен `True` (по умолчанию), создаётся новый объект блокировки для синхронизации доступа к значению. Если *блокировка* – объект [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) или [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock), то он будет использоваться для синхронизации доступа к значению. Если *блокировка* равен `False`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».854855Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.856857#### `multiprocessing.sharedctypes.copy(obj)`858859Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes *obj*.860861#### `multiprocessing.sharedctypes.synchronized(obj[, lock])`862863Возвращает процесс-безопасную обёртку для объекта ctypes, которая использует *блокировка* для синхронизации доступа. Если *блокировка* равен `None` (по умолчанию), объект [`multiprocessing.RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) создаётся автоматически.864865Синхронизированная обёртка будет иметь два дополнительных метода (помимо методов обёрнутого объекта): `get_obj()` возвращает обёрнутый объект, а `get_lock()` – объект блокировки, используемый для синхронизации.866867Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.868869В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некоторый подкласс [`ctypes.Structure`](https://python-all.ru/2.7/library/ctypes.html#ctypes.Structure).)870871| ctypes | sharedctypes с типом | sharedctypes с typecode |872| --- | --- | --- |873| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |874| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) |  |875| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |876| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |877878Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:879880```python881from multiprocessing import Process, Lock882from multiprocessing.sharedctypes import Value, Array883from ctypes import Structure, c_double884885class Point(Structure):886    _fields_ = [('x', c_double), ('y', c_double)]887888def modify(n, x, s, A):889    n.value **= 2890    x.value **= 2891    s.value = s.value.upper()892    for a in A:893        a.x **= 2894        a.y **= 2895896if __name__ == '__main__':897    lock = Lock()898899    n = Value('i', 7)900    x = Value(c_double, 1.0/3.0, lock=False)901    s = Array('c', 'hello world', lock=lock)902    A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)903904    p = Process(target=modify, args=(n, x, s, A))905    p.start()906    p.join()907908    print n.value909    print x.value910    print s.value911    print [(a.x, a.y) for a in A]912```913914Выводятся следующие результаты:915916```text917499180.1111111111111111919HELLO WORLD920[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]921```922923### 16.6.2.7. Менеджеры924925Менеджеры позволяют создавать данные, которыми могут обмениваться разные процессы. Объект менеджера управляет серверным процессом, который ведает *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.926927#### `multiprocessing.Manager()`928929Возвращает запущенный объект [`SyncManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.SyncManager), который можно использовать для совместного использования объектов между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.930931Процессы менеджера завершаются, как только они будут собраны сборщиком мусора или завершится их родительский процесс. Классы менеджера определены в модуле [`multiprocessing.managers`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.managers):932933#### `class multiprocessing.managers.BaseManager([address[, authkey]])`934935Создает объект BaseManager.936937После создания необходимо вызвать [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или `get_server().serve_forever()`, чтобы объект менеджера ссылался на запущенный процесс менеджера.938939*address* – адрес, на котором процесс менеджера ожидает новые подключения. Если *address* равно `None`, то выбирается произвольный адрес.940941*authkey* – это ключ аутентификации, который будет использоваться для проверки подлинности входящих соединений с серверным процессом. Если *authkey* равен `None`, то `current_process().authkey`. В противном случае используется *authkey*, и он должен быть строкой.942943#### `start([initializer[, initargs]])`944945Запускает подпроцесс для запуска менеджера. Если *initializer* не равно `None`, то подпроцесс вызовет `initializer(*initargs)` при запуске.946947#### `get_server()`948949Возвращает объект `Server`, представляющий реальный сервер, управляемый менеджером. Объект `Server` поддерживает метод `serve_forever()`:950951```python952>>> from multiprocessing.managers import BaseManager953>>> manager = BaseManager(address=('', 50000), authkey='abc')954>>> server = manager.get_server()955>>> server.serve_forever()956```957958`Server` также имеет атрибут [`address`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).959960#### `connect()`961962Подключает локальный объект менеджера к удаленному процессу менеджера:963964```python965>>> from multiprocessing.managers import BaseManager966>>> m = BaseManager(address=('127.0.0.1', 5000), authkey='abc')967>>> m.connect()968```969970#### `shutdown()`971972Останавливает процесс, используемый менеджером. Это доступно только если [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.973974Этот метод можно вызывать несколько раз.975976#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`977978Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.979980*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.981982*callable* – это вызываемый объект, используемый для создания объектов данного идентификатора типа. Если экземпляр менеджера будет создан с помощью метода класса `from_address()` или если аргумент *create\_method* равен `False`, то его можно оставить как `None`.983984*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.985986*exposed* используется для указания последовательности имен методов, к которым прокси для данного typeid должны иметь доступ через [`BaseProxy._callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). (Если *exposed* равно `None`, то вместо него используется `proxytype._exposed_`, если он существует.) Если список exposed не указан, то все «публичные методы» разделяемого объекта будут доступны. (Здесь «публичный метод» означает любой атрибут, у которого есть метод [`__call__()`](https://python-all.ru/2.7/reference/datamodel.html#object.__call__) и имя которого не начинается с `'_'`.)987988*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно отображает имена методов в строки typeid. (Если *method\_to\_typeid* равно `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом этого отображения или отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.989990*create\_method* определяет, следует ли создать метод с именем *typeid*, который можно использовать, чтобы указать серверному процессу создать новый разделяемый объект и вернуть для него прокси. По умолчанию `True`.991992Экземпляры [`BaseManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:993994#### `address`995996Адрес, используемый менеджером.997998#### `class multiprocessing.managers.SyncManager`9991000Подкласс [`BaseManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.10011002Он также поддерживает создание разделяемых списков и словарей.10031004#### `BoundedSemaphore([value])`10051006Создать общий объект [`threading.BoundedSemaphore`](https://python-all.ru/2.7/library/threading.html#threading.BoundedSemaphore) и вернуть для него прокси.10071008#### `Condition([lock])`10091010Создать общий объект [`threading.Condition`](https://python-all.ru/2.7/library/threading.html#threading.Condition) и вернуть прокси для него.10111012Если *блокировка* предоставлена, то она должна быть прокси для объекта [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock) или [`threading.RLock`](https://python-all.ru/2.7/library/threading.html#threading.RLock).10131014#### `Event()`10151016Создать общий объект [`threading.Event`](https://python-all.ru/2.7/library/threading.html#threading.Event) и вернуть прокси для него.10171018#### `Lock()`10191020Создать общий объект [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock) и вернуть прокси для него.10211022#### `Namespace()`10231024Создать общий объект [`Namespace`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.Namespace) и вернуть прокси для него.10251026#### `Queue([maxsize])`10271028Создать общий объект [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue) и вернуть прокси для него.10291030#### `RLock()`10311032Создать общий объект [`threading.RLock`](https://python-all.ru/2.7/library/threading.html#threading.RLock) и вернуть прокси для него.10331034#### `Semaphore([value])`10351036Создать общий объект [`threading.Semaphore`](https://python-all.ru/2.7/library/threading.html#threading.Semaphore) и вернуть прокси для него.10371038#### `Array(typecode, sequence)`10391040Создать массив и вернуть для него прокси.10411042#### `Value(typecode, value)`10431044Создать объект с атрибутом `value`, доступным для записи, и вернуть прокси для него.10451046#### `dict()`10471048#### `dict(mapping)`10491050#### `dict(sequence)`10511052Создать общий объект `dict` и вернуть прокси для него.10531054#### `list()`10551056#### `list(sequence)`10571058Создать общий объект `list` и вернуть прокси для него.10591060> **Примечание**1061>1062> Изменения изменяемых значений или элементов в прокси-объектах словарей и списков не будут распространяться через менеджер, поскольку прокси не может отследить, когда его значения или элементы изменяются. Чтобы изменить такой элемент, можно переназначить изменённый объект обратно в прокси-контейнер:1063>1064> ```python1065> # создать прокси для списка и добавить изменяемый объект (словарь)1066> lproxy = manager.list()1067> lproxy.append({})1068> # теперь изменить словарь1069> d = lproxy[0]1070> d['a'] = 11071> d['b'] = 21072> # на данный момент изменения в d еще не синхронизированы, но при1073> # при переприсваивании словаря прокси получает уведомление об изменении1074> lproxy[0] = d1075> ```10761077#### `class multiprocessing.managers.Namespace`10781079Тип, который можно зарегистрировать с помощью [`SyncManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.SyncManager).10801081Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.10821083Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:10841085```pycon1086>>> manager = multiprocessing.Manager()1087>>> Global = manager.Namespace()1088>>> Global.x = 101089>>> Global.y = 'hello'1090>>> Global._z = 12.3    # это атрибут прокси1091>>> print Global1092Namespace(x=10, y='hello')1093```10941095#### 16.6.2.7.1. Настраиваемые менеджеры10961097Чтобы создать собственный менеджер, создаётся подкласс [`BaseManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager) и используется метод класса [`register()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.register) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:10981099```python1100from multiprocessing.managers import BaseManager11011102class MathsClass(object):1103    def add(self, x, y):1104        return x + y1105    def mul(self, x, y):1106        return x * y11071108class MyManager(BaseManager):1109    pass11101111MyManager.register('Maths', MathsClass)11121113if __name__ == '__main__':1114    manager = MyManager()1115    manager.start()1116    maths = manager.Maths()1117    print maths.add(4, 3)         # выводит 71118    print maths.mul(7, 8)         # выводит 561119```11201121#### 16.6.2.7.2. Использование удалённого менеджера11221123Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).11241125Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:11261127```python1128>>> from multiprocessing.managers import BaseManager1129>>> import Queue1130>>> queue = Queue.Queue()1131>>> class QueueManager(BaseManager): pass1132>>> QueueManager.register('get_queue', callable=lambda:queue)1133>>> m = QueueManager(address=('', 50000), authkey='abracadabra')1134>>> s = m.get_server()1135>>> s.serve_forever()1136```11371138Один клиент может получить доступ к серверу следующим образом:11391140```python1141>>> from multiprocessing.managers import BaseManager1142>>> class QueueManager(BaseManager): pass1143>>> QueueManager.register('get_queue')1144>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')1145>>> m.connect()1146>>> queue = m.get_queue()1147>>> queue.put('hello')1148```11491150Другой клиент также может использовать его:11511152```python1153>>> from multiprocessing.managers import BaseManager1154>>> class QueueManager(BaseManager): pass1155>>> QueueManager.register('get_queue')1156>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')1157>>> m.connect()1158>>> queue = m.get_queue()1159>>> queue.get()1160'hello'1161```11621163Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:11641165```python1166>>> from multiprocessing import Process, Queue1167>>> from multiprocessing.managers import BaseManager1168>>> class Worker(Process):1169...     def __init__(self, q):1170...         self.q = q1171...         super(Worker, self).__init__()1172...     def run(self):1173...         self.q.put('local hello')1174...1175>>> queue = Queue()1176>>> w = Worker(queue)1177>>> w.start()1178>>> class QueueManager(BaseManager): pass1179...1180>>> QueueManager.register('get_queue', callable=lambda: queue)1181>>> m = QueueManager(address=('', 50000), authkey='abracadabra')1182>>> s = m.get_server()1183>>> s.serve_forever()1184```11851186### 16.6.2.8. Прокси-объекты11871188Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.11891190Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:11911192```pycon1193>>> from multiprocessing import Manager1194>>> manager = Manager()1195>>> l = manager.list([i*i for i in range(10)])1196>>> print l1197[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]1198>>> print repr(l)1199<ListProxy object, typeid 'list' at 0x...>1200>>> l[4]1201161202>>> l[2:5]1203[4, 9, 16]1204```12051206Обратите внимание, что применение [`str()`](https://python-all.ru/2.7/library/functions.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/2.7/library/functions.html#repr) вернёт представление прокси.12071208Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:12091210```pycon1211>>> a = manager.list()1212>>> b = manager.list()1213>>> a.append(b)         # референт a теперь содержит референт b1214>>> print a, b1215[[]] []1216>>> b.append('hello')1217>>> print a, b1218[['hello']] ['hello']1219```12201221> **Примечание**1222>1223> Типы прокси в [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) не поддерживают сравнение по значению. Так, например:1224>1225> ```pycon1226> >>> manager.list([1,2,3]) == [1,2,3]1227> False1228> ```1229>1230> При сравнении следует просто использовать копию референта.12311232#### `class multiprocessing.managers.BaseProxy`12331234Прокси-объекты являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy).12351236#### `_callmethod(methodname[, args[, kwds]])`12371238Вызывает метод референта прокси и возвращает результат.12391240Если `proxy` – это прокси, чей референт – `obj`, то выражение12411242```python1243proxy._callmethod(methodname, args, kwds)1244```12451246вычислит выражение12471248```python1249getattr(obj, methodname)(*args, **kwds)1250```12511252в процессе менеджера.12531254Возвращаемое значение будет копией результата вызова или прокси для нового разделяемого объекта – см. документацию по аргументу *method\_to\_typeid* функции [`BaseManager.register()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).12551256Если при вызове возникло исключение, то оно повторно возбуждается с помощью [`_callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod). Если в процессе менеджера возникло другое исключение, то оно преобразуется в исключение `RemoteError` и возбуждается с помощью [`_callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod).12571258Обратите внимание, что исключение будет возбуждаться, если *methodname* не был *экспонирован*.12591260Пример использования [`_callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):12611262```pycon1263>>> l = manager.list(range(10))1264>>> l._callmethod('__len__')1265101266>>> l._callmethod('__getslice__', (2, 7))   # эквивалентно `l[2:7]`1267[2, 3, 4, 5, 6]1268>>> l._callmethod('__getitem__', (20,))     # эквивалентно `l[20]`1269Traceback (most recent call last):1270...1271IndexError: list index out of range1272```12731274#### `_getvalue()`12751276Возвращает копию референта.12771278Если референт не может быть сериализован (unpicklable), то это вызовет исключение.12791280#### `__repr__()`12811282Возвращает представление прокси-объекта.12831284#### `__str__()`12851286Возвращает представление референта.12871288#### 16.6.2.8.1. Очистка12891290Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.12911292Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.12931294### 16.6.2.9. Пулы процессов12951296Можно создать пул процессов, который будет выполнять задачи, отправленные ему с помощью класса `Pool`.12971298#### `class multiprocessing.Pool([processes[, initializer[, initargs[, maxtasksperchild]]]])`12991300Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.13011302*processes* определяет количество используемых рабочих процессов. Если *processes* равно `None`, то используется число, возвращаемое `cpu_count()`. Если *initializer* не равно `None`, то каждый рабочий процесс вызовет `initializer(*initargs)` при запуске.13031304Обратите внимание, что методы объекта пула должны вызываться только тем процессом, который создал пул.13051306Новое в версии 2.7: *maxtasksperchild* – количество задач, которое рабочий процесс может выполнить прежде чем завершится и будет заменён новым рабочим процессом, чтобы освободить неиспользуемые ресурсы. Значение по умолчанию *maxtasksperchild* равно `None`, что означает, что рабочие процессы будут жить столько же, сколько и пул.13071308> **Примечание**1309>1310> Рабочие процессы внутри `Pool` обычно существуют в течение всего времени работы очереди пула. Часто используемый в других системах (например, Apache, mod\_wsgi и т.д.) подход для освобождения ресурсов, удерживаемых рабочими процессами, заключается в том, чтобы позволить рабочему процессу в пуле выполнить только определённый объём работы, после чего он завершается, очищается, и на его место запускается новый процесс. Аргумент *maxtasksperchild* функции `Pool` предоставляет эту возможность конечному пользователю.13111312#### `apply(func[, args[, kwds]])`13131314Эквивалент встроенной функции [`apply()`](https://python-all.ru/2.7/library/functions.html#apply). Она блокируется до получения результата, поэтому [`apply_async()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.apply_async) лучше подходит для выполнения работы в параллельном режиме. Кроме того, *func* выполняется только в одном из рабочих процессов пула.13151316#### `apply_async(func[, args[, kwds[, callback]]])`13171318Вариант метода [`apply()`](https://python-all.ru/2.7/library/functions.html#apply), возвращающий объект результата.13191320Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.13211322#### `map(func, iterable[, chunksize])`13231324Параллельный аналог встроенной функции [`map()`](https://python-all.ru/2.7/library/functions.html#map) (однако поддерживает только один аргумент *iterable*). Блокирует выполнение до готовности результата.13251326Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.13271328#### `map_async(func, iterable[, chunksize[, callback]])`13291330Вариант метода [`map()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map), возвращающий объект результата.13311332Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.13331334#### `imap(func, iterable[, chunksize])`13351336An equivalent of [`itertools.imap()`](https://python-all.ru/2.7/library/itertools.html#itertools.imap).13371338Аргумент *chunksize* совпадает с тем, что используется в методе [`map()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map). Для очень длинных итерируемых объектов использование большого значения *chunksize* может привести к завершению задачи **значительно** быстрее, чем при использовании значения по умолчанию `1`.13391340Кроме того, если *chunksize* равно `1`, то метод `next()` итератора, возвращаемого методом [`imap()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` возбудит `multiprocessing.TimeoutError`, если результат не может быть возвращён в течение *timeout* секунд.13411342#### `imap_unordered(func, iterable[, chunksize])`13431344То же, что и [`imap()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), за исключением того, что порядок результатов из возвращаемого итератора считается произвольным. (Только когда есть только один рабочий процесс, порядок гарантированно будет «правильным».)13451346#### `close()`13471348Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.13491350#### `terminate()`13511352Немедленно останавливает рабочие процессы, не дожидаясь завершения незаконченных задач. Когда объект пула будет собран сборщиком мусора, [`terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate) будет вызван немедленно.13531354#### `join()`13551356Ожидает завершения рабочих процессов. Перед использованием [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.join) необходимо вызвать [`close()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.close) или [`terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate).13571358#### `class multiprocessing.pool.AsyncResult`13591360Класс результата, возвращаемого `Pool.apply_async()` и `Pool.map_async()`.13611362#### `get([timeout])`13631364Возвращает результат, когда он будет получен. Если *timeout* не равен `None` и результат не поступает в течение *timeout* секунд, то вызывается `multiprocessing.TimeoutError`. Если удалённый вызов вызвал исключение, то это исключение будет повторно возбуждено [`get()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get).13651366#### `wait([timeout])`13671368Ожидает, пока результат не станет доступен или пока не пройдёт *timeout* секунд.13691370#### `ready()`13711372Возвращает, завершён ли вызов.13731374#### `successful()`13751376Возвращает, завершился ли вызов без возникновения исключения. Возбуждает [`AssertionError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AssertionError), если результат ещё не готов.13771378Следующий пример демонстрирует использование пула:13791380```python1381from multiprocessing import Pool1382import time13831384def f(x):1385    return x*x13861387if __name__ == '__main__':1388    pool = Pool(processes=4)              # запустить 4 рабочих процесса13891390    result = pool.apply_async(f, (10,))   # вычислить "f(10)" асинхронно в одном процессе1391    print result.get(timeout=1)           # выводит "100", если компьютер не *очень* медленный13921393    print pool.map(f, range(10))          # выводит "[0, 1, 4,..., 81]"13941395    it = pool.imap(f, range(10))1396    print it.next()                       # выводит "0"1397    print it.next()                       # выводит "1"1398    print it.next(timeout=1)              # выводит "4", если компьютер не *очень* медленный13991400    result = pool.apply_async(time.sleep, (10,))1401    print result.get(timeout=1)           # вызывает multiprocessing.TimeoutError1402```14031404### 16.6.2.10. Слушатели и клиенты14051406Обычно передача сообщений между процессами осуществляется с помощью очередей или с помощью объектов [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection), возвращаемых [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe).14071408Однако модуль [`multiprocessing.connection`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.connection) обеспечивает некоторую дополнительную гибкость. По сути, он предоставляет высокоуровневый API, ориентированный на сообщения, для работы с сокетами или именованными каналами Windows, а также поддерживает *дайджест-аутентификацию* с помощью модуля [`hmac`](https://python-all.ru/2.7/library/hmac.html#module-hmac).14091410#### `multiprocessing.connection.deliver_challenge(connection, authkey)`14111412Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.14131414Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).14151416#### `multiprocessing.connection.answer_challenge(connection, authkey)`14171418Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.14191420Если приветственное сообщение не получено, то [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError) возбуждается.14211422#### `multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`14231424Пытается установить соединение с слушателем, использующим адрес *address*, возвращая [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection).14251426Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [Address Formats](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-address-formats))14271428Если *authenticate* равно `True` или *authkey* является строкой, то используется дайджест-аутентификация. Ключ аутентификации будет либо *authkey*, либо `current_process().authkey)`, если *authkey* равно `None`. Если аутентификация не удалась, возбуждается [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError). См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).14291430#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`14311432Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.14331434*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.14351436> **Примечание**1437>1438> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.14391440*family* – тип используемого сокета (или именованного канала). Может быть одной из строк `'AF_INET'` (для TCP-сокета), `'AF_UNIX'` (для сокета домена Unix) или `'AF_PIPE'` (для именованного канала Windows). Из них гарантированно доступна только первая. Если *family* равно `None`, то семейство определяется по формату *address*. Если *address* также равно `None`, выбирается значение по умолчанию. По умолчанию выбирается семейство, которое считается самым быстрым из доступных. См. [Форматы адресов](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-address-formats). Обратите внимание: если *family* равно `'AF_UNIX'`, а address равен `None`, то сокет будет создан в частной временной директории, созданной с помощью [`tempfile.mkstemp()`](https://python-all.ru/2.7/library/tempfile.html#tempfile.mkstemp).14411442Если объект listener использует сокет, то *backlog* (по умолчанию 1) передаётся методу [`listen()`](https://python-all.ru/2.7/library/socket.html#socket.socket.listen) сокета после его привязки.14431444Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.14451446Если *authkey* является строкой, то она будет использоваться в качестве ключа аутентификации; в противном случае она должна быть `None`.14471448Если *authkey* равно `None` и *authenticate* равно `True`, то `current_process().authkey` используется в качестве ключа аутентификации. Если *authkey* равно `None` и *authenticate* равно `False`, то аутентификация не выполняется. Если аутентификация не удалась, то [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError) возбуждается. См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).14491450#### `accept()`14511452Принимает соединение на привязанном сокете или именованном канале объекта listener и возвращает объект [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection). Если предпринята попытка аутентификации и она не удалась, возбуждается `AuthenticationError`.14531454#### `close()`14551456Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.14571458Объекты Listener имеют следующие свойства только для чтения:14591460#### `address`14611462Адрес, используемый объектом Listener.14631464#### `last_accepted`14651466Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то равен `None`.14671468Модуль определяет следующие исключения:14691470#### `exception multiprocessing.connection.ProcessError`14711472Базовый класс всех исключений [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing).14731474#### `exception multiprocessing.connection.BufferTooShort`14751476Исключение, возбуждаемое [`Connection.recv_bytes_into()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv_bytes_into), когда предоставленный буфер слишком мал для чтения сообщения.14771478#### `exception multiprocessing.connection.AuthenticationError`14791480Возбуждается при ошибке аутентификации.14811482#### `exception multiprocessing.connection.TimeoutError`14831484Возбуждается методами с тайм-аутом по истечении тайм-аута.14851486**Примеры**14871488Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:14891490```python1491from multiprocessing.connection import Listener1492from array import array14931494address = ('localhost', 6000)     # семейство определяется как 'AF_INET'1495listener = Listener(address, authkey='secret password')14961497conn = listener.accept()1498print 'connection accepted from', listener.last_accepted14991500conn.send([2.25, None, 'junk', float])15011502conn.send_bytes('hello')15031504conn.send_bytes(array('i', [42, 1729]))15051506conn.close()1507listener.close()1508```15091510Следующий код подключается к серверу и получает от него некоторые данные:15111512```python1513from multiprocessing.connection import Client1514from array import array15151516address = ('localhost', 6000)1517conn = Client(address, authkey='secret password')15181519print conn.recv()                 # => [2.25, None, 'junk', float]15201521print conn.recv_bytes()            # => 'hello'15221523arr = array('i', [0, 0, 0, 0, 0])1524print conn.recv_bytes_into(arr)     # => 81525print arr                         # => array('i', [42, 1729, 0, 0, 0])15261527conn.close()1528```15291530#### 16.6.2.10.1. Форматы адресов15311532- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.1533- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.1534- **Адрес `'AF_PIPE'` – это строка вида**15351536  `r'\.\pipe{PipeName}'`. Чтобы использовать [`Client()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.Client) для подключения к именованному каналу на удалённом компьютере *ServerName*, следует использовать адрес вида `r'\ServerName\pipe{PipeName}'`.15371538Обратите внимание: любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не `'AF_UNIX'`.15391540### 16.6.2.11. Ключи аутентификации15411542При использовании [`Connection.recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv), полученные данные автоматически десериализуются. К сожалению, десериализация данных из ненадёжного источника представляет угрозу безопасности. Поэтому [`Listener`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.Listener) и [`Client()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.Client) используют модуль [`hmac`](https://python-all.ru/2.7/library/hmac.html#module-hmac) для обеспечения дайджест-аутентификации.15431544Ключ аутентификации – это строка, которую можно рассматривать как пароль: как только соединение установлено, обе стороны требуют подтверждения, что другая сторона знает ключ аутентификации. (Доказательство того, что обе стороны используют один и тот же ключ, **не** подразумевает отправку ключа по соединению.)15451546Если запрошена аутентификация, но ключ аутентификации не указан, то используется возвращаемое значение `current_process().authkey` (см. [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process)). Это значение автоматически наследуется любым объектом [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process), который создаёт текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы используют один ключ аутентификации, который можно применять для настройки соединений между ними.15471548Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/2.7/library/os.html#os.urandom).15491550### 16.6.2.12. Логирование15511552Доступна поддержка журналирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/2.7/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому сообщения от разных процессов могут перемешиваться (в зависимости от типа обработчика).15531554#### `multiprocessing.get_logger()`15551556Возвращает логгер, используемый модулем [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing). При необходимости создаётся новый.15571558При первом создании логгер имеет уровень `logging.NOTSET` и не имеет обработчика по умолчанию. Сообщения, отправленные этому логгеру, по умолчанию не распространяются на корневой логгер.15591560Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.15611562#### `multiprocessing.log_to_stderr()`15631564Эта функция вызывает [`get_logger()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.get_logger), но помимо возврата регистратора, созданного get\_logger, добавляет обработчик, который отправляет вывод на [`sys.stderr`](https://python-all.ru/2.7/library/sys.html#sys.stderr), используя формат `'[%(levelname)s/%(processName)s] %(message)s'`.15651566Ниже приведён пример сеанса с включённым журналированием:15671568```python1569>>> import multiprocessing, logging1570>>> logger = multiprocessing.log_to_stderr()1571>>> logger.setLevel(logging.INFO)1572>>> logger.warning('doomed')1573[WARNING/MainProcess] doomed1574>>> m = multiprocessing.Manager()1575[INFO/SyncManager-...] child process calling self.run()1576[INFO/SyncManager-...] created temp directory /.../pymp-...1577[INFO/SyncManager-...] manager serving at '/.../listener-...'1578>>> del m1579[INFO/MainProcess] sending shutdown message to manager1580[INFO/SyncManager-...] manager exiting with exitcode 01581```15821583В дополнение к этим двум функциям логирования, модуль multiprocessing также предоставляет два дополнительных атрибута уровня логирования: `SUBWARNING` и `SUBDEBUG`. В таблице ниже показано, где они находятся в обычной иерархии уровней.15841585| Уровень | Числовое значение |1586| --- | --- |1587| `SUBWARNING` | 25 |1588| `SUBDEBUG` | 5 |15891590Полную таблицу уровней журналирования см. в модуле [`logging`](https://python-all.ru/2.7/library/logging.html#module-logging).15911592Эти дополнительные уровни логирования используются в основном для определённых отладочных сообщений в модуле multiprocessing. Ниже приведён тот же пример, что и выше, но с включённым `SUBDEBUG`:15931594```python1595>>> import multiprocessing, logging1596>>> logger = multiprocessing.log_to_stderr()1597>>> logger.setLevel(multiprocessing.SUBDEBUG)1598>>> logger.warning('doomed')1599[WARNING/MainProcess] doomed1600>>> m = multiprocessing.Manager()1601[INFO/SyncManager-...] child process calling self.run()1602[INFO/SyncManager-...] created temp directory /.../pymp-...1603[INFO/SyncManager-...] manager serving at '/.../pymp-djGBXN/listener-...'1604>>> del m1605[SUBDEBUG/MainProcess] finalizer calling ...1606[INFO/MainProcess] sending shutdown message to manager1607[DEBUG/SyncManager-...] manager received shutdown message1608[SUBDEBUG/SyncManager-...] calling <Finalize object, callback=unlink, ...1609[SUBDEBUG/SyncManager-...] finalizer calling <built-in function unlink> ...1610[SUBDEBUG/SyncManager-...] calling <Finalize object, dead>1611[SUBDEBUG/SyncManager-...] finalizer calling <function rmtree at 0x5aa730> ...1612[INFO/SyncManager-...] manager exiting with exitcode 01613```16141615### 16.6.2.13. Модуль [`multiprocessing.dummy`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.dummy)16161617[`multiprocessing.dummy`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.dummy) воспроизводит API модуля [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing), но является не более чем обёрткой вокруг модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).16181619## 16.6.3. Рекомендации по программированию16201621Существуют определённые рекомендации и идиомы, которых следует придерживаться при использовании [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing).16221623### 16.6.3.1. Все платформы16241625Избегайте разделяемого состояния16261627> По возможности следует стараться избегать передачи больших объёмов данных между процессами.1628>1629> Вероятно, лучше придерживаться использования очередей или каналов для связи между процессами, а не использовать низкоуровневые примитивы синхронизации из модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).16301631Сериализуемость16321633> Убедитесь, что аргументы методов прокси-объектов сериализуемы.16341635Потокобезопасность прокси16361637> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.1638>1639> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)16401641Присоединение процессов-зомби16421643> В Unix, когда процесс завершается, но к нему не присоединились (joined), он становится зомби. Таких процессов не должно быть много, потому что каждый раз при запуске нового процесса (или вызове [`active_children()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.active_children)) все завершённые процессы, к которым ещё не присоединились, будут присоединены. Кроме того, вызов [`Process.is_alive`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.is_alive) для завершённого процесса присоединяет его. Тем не менее, рекомендуется явно присоединять все запущенные процессы.16441645Лучше наследовать, чем упаковывать/распаковывать16461647> В Windows многие типы из [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) должны быть сериализуемыми (picklable), чтобы дочерние процессы могли их использовать. Однако обычно следует избегать отправки общих объектов другим процессам через каналы или очереди. Вместо этого следует организовать программу так, чтобы процесс, которому требуется доступ к общему ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.16481649Избегайте завершения процессов16501651> Использование метода [`Process.terminate`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), которые в данный момент используются процессом, станут повреждёнными или недоступными для других процессов.1652>1653> Поэтому, вероятно, лучше рассматривать использование [`Process.terminate`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) только для процессов, которые никогда не используют разделяемые ресурсы.16541655Присоединение процессов, использующих очереди16561657> Имейте в виду, что процесс, поместивший элементы в очередь, будет ждать перед завершением, пока все буферизированные элементы не будут переданы потоком «feeder» в базовый канал. (Дочерний процесс может вызвать метод [`cancel_join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread) очереди, чтобы избежать такого поведения.)1658>1659> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещённые в очередь, будут в конечном итоге удалены до того, как процесс будет присоединён. В противном случае нельзя быть уверенным, что процессы, поместившие элементы в очередь, завершатся. Также помните, что недемонические процессы присоединяются автоматически.1660>1661> Пример, который приведёт к взаимоблокировке:1662>1663> ```python1664> from multiprocessing import Process, Queue1665>1666> def f(q):1667>     q.put('X' * 1000000)1668>1669> if __name__ == '__main__':1670>     queue = Queue()1671>     p = Process(target=f, args=(queue,))1672>     p.start()1673>     p.join()                    # это приводит к взаимоблокировке1674>     obj = queue.get()1675> ```1676>1677> Исправление заключается в замене двух последних строк местами (или просто удалении строки с `p.join()`).16781679Явная передача ресурсов дочерним процессам16801681> В Unix дочерний процесс может использовать общий ресурс, созданный в родительском процессе, с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.1682>1683> Помимо обеспечения (потенциальной) совместимости с Windows, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если какой-либо ресурс освобождается при сборке мусора в родительском процессе.1684>1685> Так, например1686>1687> ```python1688> from multiprocessing import Process, Lock1689>1690> def f():1691>     ... do something using "lock" ...1692>1693> if __name__ == '__main__':1694>     lock = Lock()1695>     for i in range(10):1696>         Process(target=f).start()1697> ```1698>1699> следует переписать как1700>1701> ```python1702> from multiprocessing import Process, Lock1703>1704> def f(l):1705>     ... do something using "l" ...1706>1707> if __name__ == '__main__':1708>     lock = Lock()1709>     for i in range(10):1710>         Process(target=f, args=(lock,)).start()1711> ```17121713Остерегайтесь замены [`sys.stdin`](https://python-all.ru/2.7/library/sys.html#sys.stdin) на «файлоподобный объект»17141715> [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) изначально безусловно вызывал:1716>1717> ```python1718> os.close(sys.stdin.fileno())1719> ```1720>1721> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:1722>1723> ```python1724> sys.stdin.close()1725> sys.stdin = open(os.devnull)1726> ```1727>1728> Это решает основную проблему столкновения процессов друг с другом, приводящую к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, которые заменяют [`sys.stdin()`](https://python-all.ru/2.7/library/sys.html#sys.stdin) на «файлоподобный объект» с буферизацией вывода. Опасность в том, что если несколько процессов вызовут [`close()`](https://python-all.ru/2.7/library/io.html#io.IOBase.close) для этого файлоподобного объекта, одни и те же данные могут быть сброшены в объект несколько раз, что приведёт к повреждению.1729>1730> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:1731>1732> ```python1733> @property1734> def cache(self):1735>     pid = os.getpid()1736>     if pid != self._pid:1737>         self._pid = pid1738>         self._cache = []1739>     return self._cache1740> ```1741>1742> Для получения дополнительной информации см. [bpo-5155](https://python-all.ru/2.7/library/multiprocessing.html), [bpo-5313](https://python-all.ru/2.7/library/multiprocessing.html) и [bpo-5331](https://python-all.ru/2.7/library/multiprocessing.html)17431744### 16.6.3.2. Windows17451746Поскольку в Windows отсутствует [`os.fork()`](https://python-all.ru/2.7/library/os.html#os.fork), у него есть несколько дополнительных ограничений:17471748Упаковываемость17491750> Убедитесь, что все аргументы `Process.__init__()` сериализуемы (picklable). Это означает, в частности, что связанные или несвязанные методы не могут использоваться напрямую в качестве аргумента `target` в Windows – просто определите функцию и используйте её вместо этого.1751>1752> Кроме того, если вы создаёте подкласс [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process), убедитесь, что экземпляры будут сериализуемы (picklable) при вызове метода [`Process.start`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start).17531754Глобальные переменные17551756> Имейте в виду, что если код, выполняемый в дочернем процессе, попытается обратиться к глобальной переменной, то значение, которое он увидит (если оно есть), может не совпадать со значением в родительском процессе на момент вызова [`Process.start`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start).1757>1758> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.17591760Безопасный импорт главного модуля17611762> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).1763>1764> Например, в Windows при запуске следующего модуля возникнет ошибка [`RuntimeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.RuntimeError):1765>1766> ```python1767> from multiprocessing import Process1768>1769> def foo():1770>     print 'hello'1771>1772> p = Process(target=foo)1773> p.start()1774> ```1775>1776> Вместо этого следует защитить «точку входа» программы с помощью `if __name__ == '__main__':` следующим образом:1777>1778> ```python1779> from multiprocessing import Process, freeze_support1780>1781> def foo():1782>     print 'hello'1783>1784> if __name__ == '__main__':1785>     freeze_support()1786>     p = Process(target=foo)1787>     p.start()1788> ```1789>1790> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не как замороженное приложение.)1791>1792> Это позволяет только что порождённому интерпретатору Python безопасно импортировать модуль и затем выполнить функцию `foo()` этого модуля.1793>1794> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.17951796## 16.6.4. Примеры17971798Демонстрация создания и использования настраиваемых менеджеров и прокси:17991800```python1801#1802# Этот модуль показывает, как использовать произвольные вызываемые объекты с подклассом `BaseManager`.1803# `BaseManager`.1804#1805# Авторское право (c) 2006-2008, R Oudkerk1806# Все права защищены.1807#18081809from multiprocessing import freeze_support1810from multiprocessing.managers import BaseManager, BaseProxy1811import operator18121813##18141815class Foo(object):1816    def f(self):1817        print 'you called Foo.f()'1818    def g(self):1819        print 'you called Foo.g()'1820    def _h(self):1821        print 'you called Foo._h()'18221823# Простая функция-генератор1824def baz():1825    for i in xrange(10):1826        yield i*i18271828# Тип прокси для объектов-генераторов1829class GeneratorProxy(BaseProxy):1830    _exposed_ = ('next', '__next__')1831    def __iter__(self):1832        return self1833    def next(self):1834        return self._callmethod('next')1835    def __next__(self):1836        return self._callmethod('__next__')18371838# Функция для возврата модуля operator1839def get_operator_module():1840    return operator18411842##18431844class MyManager(BaseManager):1845    pass18461847# зарегистрировать класс Foo; сделать `f()` и `g()` доступными через прокси1848MyManager.register('Foo1', Foo)18491850# зарегистрировать класс Foo; сделать `g()` и `_h()` доступными через прокси1851MyManager.register('Foo2', Foo, exposed=('g', '_h'))18521853# зарегистрировать функцию-генератор baz; использовать `GeneratorProxy` для создания прокси1854MyManager.register('baz', baz, proxytype=GeneratorProxy)18551856# зарегистрировать get_operator_module(); сделать публичные функции доступными через прокси1857MyManager.register('operator', get_operator_module)18581859##18601861def test():1862    manager = MyManager()1863    manager.start()18641865    print '-' * 2018661867    f1 = manager.Foo1()1868    f1.f()1869    f1.g()1870    assert not hasattr(f1, '_h')1871    assert sorted(f1._exposed_) == sorted(['f', 'g'])18721873    print '-' * 2018741875    f2 = manager.Foo2()1876    f2.g()1877    f2._h()1878    assert not hasattr(f2, 'f')1879    assert sorted(f2._exposed_) == sorted(['g', '_h'])18801881    print '-' * 2018821883    it = manager.baz()1884    for i in it:1885        print '<%d>' % i,1886    print18871888    print '-' * 2018891890    op = manager.operator()1891    print 'op.add(23, 45) =', op.add(23, 45)1892    print 'op.pow(2, 94) =', op.pow(2, 94)1893    print 'op.getslice(range(10), 2, 6) =', op.getslice(range(10), 2, 6)1894    print 'op.repeat(range(5), 3) =', op.repeat(range(5), 3)1895    print 'op._exposed_ =', op._exposed_18961897##18981899if __name__ == '__main__':1900    freeze_support()1901    test()1902```19031904Использование `Pool`:19051906```python1907#1908# Тест класса `multiprocessing.Pool`1909#1910# Авторское право (c) 2006-2008, R Oudkerk1911# Все права защищены.1912#19131914import multiprocessing1915import time1916import random1917import sys19181919#1920# Функции, используемые тестовым кодом1921#19221923def calculate(func, args):1924    result = func(*args)1925    return '%s says that %s%s = %s' % (1926        multiprocessing.current_process().name,1927        func.__name__, args, result1928        )19291930def calculatestar(args):1931    return calculate(*args)19321933def mul(a, b):1934    time.sleep(0.5*random.random())1935    return a * b19361937def plus(a, b):1938    time.sleep(0.5*random.random())1939    return a + b19401941def f(x):1942    return 1.0 / (x-5.0)19431944def pow3(x):1945    return x**319461947def noop(x):1948    pass19491950#1951# Тестовый код1952#19531954def test():1955    print 'cpu_count() = %d\n' % multiprocessing.cpu_count()19561957    #1958    # Создать пул1959    #19601961    PROCESSES = 41962    print 'Creating pool with %d processes\n' % PROCESSES1963    pool = multiprocessing.Pool(PROCESSES)1964    print 'pool = %s' % pool1965    print19661967    #1968    # Тесты1969    #19701971    TASKS = [(mul, (i, 7)) for i in range(10)] + \1972            [(plus, (i, 8)) for i in range(10)]19731974    results = [pool.apply_async(calculate, t) for t in TASKS]1975    imap_it = pool.imap(calculatestar, TASKS)1976    imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)19771978    print 'Ordered results using pool.apply_async():'1979    for r in results:1980        print '\t', r.get()1981    print19821983    print 'Ordered results using pool.imap():'1984    for x in imap_it:1985        print '\t', x1986    print19871988    print 'Unordered results using pool.imap_unordered():'1989    for x in imap_unordered_it:1990        print '\t', x1991    print19921993    print 'Ordered results using pool.map() --- will block till complete:'1994    for x in pool.map(calculatestar, TASKS):1995        print '\t', x1996    print19971998    #1999    # Простые тесты производительности2000    #20012002    N = 1000002003    print 'def pow3(x): return x**3'20042005    t = time.time()2006    A = map(pow3, xrange(N))2007    print '\tmap(pow3, xrange(%d)):\n\t\t%s seconds' % \2008          (N, time.time() - t)20092010    t = time.time()2011    B = pool.map(pow3, xrange(N))2012    print '\tpool.map(pow3, xrange(%d)):\n\t\t%s seconds' % \2013          (N, time.time() - t)20142015    t = time.time()2016    C = list(pool.imap(pow3, xrange(N), chunksize=N//8))2017    print '\tlist(pool.imap(pow3, xrange(%d), chunksize=%d)):\n\t\t%s' \2018          ' seconds' % (N, N//8, time.time() - t)20192020    assert A == B == C, (len(A), len(B), len(C))2021    print20222023    L = [None] * 10000002024    print 'def noop(x): pass'2025    print 'L = [None] * 1000000'20262027    t = time.time()2028    A = map(noop, L)2029    print '\tmap(noop, L):\n\t\t%s seconds' % \2030          (time.time() - t)20312032    t = time.time()2033    B = pool.map(noop, L)2034    print '\tpool.map(noop, L):\n\t\t%s seconds' % \2035          (time.time() - t)20362037    t = time.time()2038    C = list(pool.imap(noop, L, chunksize=len(L)//8))2039    print '\tlist(pool.imap(noop, L, chunksize=%d)):\n\t\t%s seconds' % \2040          (len(L)//8, time.time() - t)20412042    assert A == B == C, (len(A), len(B), len(C))2043    print20442045    del A, B, C, L20462047    #2048    # Тестирование обработки ошибок2049    #20502051    print 'Testing error handling:'20522053    try:2054        print pool.apply(f, (5,))2055    except ZeroDivisionError:2056        print '\tGot ZeroDivisionError as expected from pool.apply()'2057    else:2058        raise AssertionError('expected ZeroDivisionError')20592060    try:2061        print pool.map(f, range(10))2062    except ZeroDivisionError:2063        print '\tGot ZeroDivisionError as expected from pool.map()'2064    else:2065        raise AssertionError('expected ZeroDivisionError')20662067    try:2068        print list(pool.imap(f, range(10)))2069    except ZeroDivisionError:2070        print '\tGot ZeroDivisionError as expected from list(pool.imap())'2071    else:2072        raise AssertionError('expected ZeroDivisionError')20732074    it = pool.imap(f, range(10))2075    for i in range(10):2076        try:2077            x = it.next()2078        except ZeroDivisionError:2079            if i == 5:2080                pass2081        except StopIteration:2082            break2083        else:2084            if i == 5:2085                raise AssertionError('expected ZeroDivisionError')20862087    assert i == 92088    print '\tGot ZeroDivisionError as expected from IMapIterator.next()'2089    print20902091    #2092    # Тестирование тайм-аутов2093    #20942095    print 'Testing ApplyResult.get() with timeout:',2096    res = pool.apply_async(calculate, TASKS[0])2097    while 1:2098        sys.stdout.flush()2099        try:2100            sys.stdout.write('\n\t%s' % res.get(0.02))2101            break2102        except multiprocessing.TimeoutError:2103            sys.stdout.write('.')2104    print2105    print21062107    print 'Testing IMapIterator.next() with timeout:',2108    it = pool.imap(calculatestar, TASKS)2109    while 1:2110        sys.stdout.flush()2111        try:2112            sys.stdout.write('\n\t%s' % it.next(0.02))2113        except StopIteration:2114            break2115        except multiprocessing.TimeoutError:2116            sys.stdout.write('.')2117    print2118    print21192120    #2121    # Тестирование колбэка2122    #21232124    print 'Testing callback:'21252126    A = []2127    B = [56, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]21282129    r = pool.apply_async(mul, (7, 8), callback=A.append)2130    r.wait()21312132    r = pool.map_async(pow3, range(10), callback=A.extend)2133    r.wait()21342135    if A == B:2136        print '\tcallbacks succeeded\n'2137    else:2138        print '\t*** callbacks failed\n\t\t%s != %s\n' % (A, B)21392140    #2141    # Проверить, что нет незавершённых задач2142    #21432144    assert not pool._cache, 'cache = %r' % pool._cache21452146    #2147    # Проверить методы close()2148    #21492150    print 'Testing close():'21512152    for worker in pool._pool:2153        assert worker.is_alive()21542155    result = pool.apply_async(time.sleep, [0.5])2156    pool.close()2157    pool.join()21582159    assert result.get() is None21602161    for worker in pool._pool:2162        assert not worker.is_alive()21632164    print '\tclose() succeeded\n'21652166    #2167    # Проверить метод terminate()2168    #21692170    print 'Testing terminate():'21712172    pool = multiprocessing.Pool(2)2173    DELTA = 0.12174    ignore = pool.apply(pow3, [2])2175    results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]2176    pool.terminate()2177    pool.join()21782179    for worker in pool._pool:2180        assert not worker.is_alive()21812182    print '\tterminate() succeeded\n'21832184    #2185    # Проверить сборку мусора2186    #21872188    print 'Testing garbage collection:'21892190    pool = multiprocessing.Pool(2)2191    DELTA = 0.12192    processes = pool._pool2193    ignore = pool.apply(pow3, [2])2194    results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]21952196    results = pool = None21972198    time.sleep(DELTA * 2)21992200    for worker in processes:2201        assert not worker.is_alive()22022203    print '\tgarbage collection succeeded\n'22042205if __name__ == '__main__':2206    multiprocessing.freeze_support()22072208    assert len(sys.argv) in (1, 2)22092210    if len(sys.argv) == 1 or sys.argv[1] == 'processes':2211        print ' Using processes '.center(79, '-')2212    elif sys.argv[1] == 'threads':2213        print ' Using threads '.center(79, '-')2214        import multiprocessing.dummy as multiprocessing2215    else:2216        print 'Usage:\n\t%s [processes | threads]' % sys.argv[0]2217        raise SystemExit(2)22182219    test()2220```22212222Типы синхронизации, такие как блокировки, условия и очереди:22232224```python2225#2226# Тестовый файл для пакета `multiprocessing`2227#2228# Авторское право (c) 2006-2008, R Oudkerk2229# Все права защищены.2230#22312232import time, sys, random2233from Queue import Empty22342235import multiprocessing               # может быть перезаписан22362237#### TEST_VALUE22382239def value_func(running, mutex):2240    random.seed()2241    time.sleep(random.random()*4)22422243    mutex.acquire()2244    print '\n\t\t\t' + str(multiprocessing.current_process()) + ' has finished'2245    running.value -= 12246    mutex.release()22472248def test_value():2249    TASKS = 102250    running = multiprocessing.Value('i', TASKS)2251    mutex = multiprocessing.Lock()22522253    for i in range(TASKS):2254        p = multiprocessing.Process(target=value_func, args=(running, mutex))2255        p.start()22562257    while running.value > 0:2258        time.sleep(0.08)2259        mutex.acquire()2260        print running.value,2261        sys.stdout.flush()2262        mutex.release()22632264    print2265    print 'No more running processes'22662267#### TEST_QUEUE22682269def queue_func(queue):2270    for i in range(30):2271        time.sleep(0.5 * random.random())2272        queue.put(i*i)2273    queue.put('STOP')22742275def test_queue():2276    q = multiprocessing.Queue()22772278    p = multiprocessing.Process(target=queue_func, args=(q,))2279    p.start()22802281    o = None2282    while o != 'STOP':2283        try:2284            o = q.get(timeout=0.3)2285            print o,2286            sys.stdout.flush()2287        except Empty:2288            print 'TIMEOUT'22892290    print22912292#### TEST_CONDITION22932294def condition_func(cond):2295    cond.acquire()2296    print '\t' + str(cond)2297    time.sleep(2)2298    print '\tchild is notifying'2299    print '\t' + str(cond)2300    cond.notify()2301    cond.release()23022303def test_condition():2304    cond = multiprocessing.Condition()23052306    p = multiprocessing.Process(target=condition_func, args=(cond,))2307    print cond23082309    cond.acquire()2310    print cond2311    cond.acquire()2312    print cond23132314    p.start()23152316    print 'main is waiting'2317    cond.wait()2318    print 'main has woken up'23192320    print cond2321    cond.release()2322    print cond2323    cond.release()23242325    p.join()2326    print cond23272328#### TEST_SEMAPHORE23292330def semaphore_func(sema, mutex, running):2331    sema.acquire()23322333    mutex.acquire()2334    running.value += 12335    print running.value, 'tasks are running'2336    mutex.release()23372338    random.seed()2339    time.sleep(random.random()*2)23402341    mutex.acquire()2342    running.value -= 12343    print '%s has finished' % multiprocessing.current_process()2344    mutex.release()23452346    sema.release()23472348def test_semaphore():2349    sema = multiprocessing.Semaphore(3)2350    mutex = multiprocessing.RLock()2351    running = multiprocessing.Value('i', 0)23522353    processes = [2354        multiprocessing.Process(target=semaphore_func,2355                                args=(sema, mutex, running))2356        for i in range(10)2357        ]23582359    for p in processes:2360        p.start()23612362    for p in processes:2363        p.join()23642365#### TEST_JOIN_TIMEOUT23662367def join_timeout_func():2368    print '\tchild sleeping'2369    time.sleep(5.5)2370    print '\n\tchild terminating'23712372def test_join_timeout():2373    p = multiprocessing.Process(target=join_timeout_func)2374    p.start()23752376    print 'waiting for process to finish'23772378    while 1:2379        p.join(timeout=1)2380        if not p.is_alive():2381            break2382        print '.',2383        sys.stdout.flush()23842385#### TEST_EVENT23862387def event_func(event):2388    print '\t%r is waiting' % multiprocessing.current_process()2389    event.wait()2390    print '\t%r has woken up' % multiprocessing.current_process()23912392def test_event():2393    event = multiprocessing.Event()23942395    processes = [multiprocessing.Process(target=event_func, args=(event,))2396                 for i in range(5)]23972398    for p in processes:2399        p.start()24002401    print 'main is sleeping'2402    time.sleep(2)24032404    print 'main is setting event'2405    event.set()24062407    for p in processes:2408        p.join()24092410#### TEST_SHAREDVALUES24112412def sharedvalues_func(values, arrays, shared_values, shared_arrays):2413    for i in range(len(values)):2414        v = values[i][1]2415        sv = shared_values[i].value2416        assert v == sv24172418    for i in range(len(values)):2419        a = arrays[i][1]2420        sa = list(shared_arrays[i][:])2421        assert a == sa24222423    print 'Tests passed'24242425def test_sharedvalues():2426    values = [2427        ('i', 10),2428        ('h', -2),2429        ('d', 1.25)2430        ]2431    arrays = [2432        ('i', range(100)),2433        ('d', [0.25 * i for i in range(100)]),2434        ('H', range(1000))2435        ]24362437    shared_values = [multiprocessing.Value(id, v) for id, v in values]2438    shared_arrays = [multiprocessing.Array(id, a) for id, a in arrays]24392440    p = multiprocessing.Process(2441        target=sharedvalues_func,2442        args=(values, arrays, shared_values, shared_arrays)2443        )2444    p.start()2445    p.join()24462447    assert p.exitcode == 024482449####24502451def test(namespace=multiprocessing):2452    global multiprocessing24532454    multiprocessing = namespace24552456    for func in [ test_value, test_queue, test_condition,2457                  test_semaphore, test_join_timeout, test_event,2458                  test_sharedvalues ]:24592460        print '\n\t######## %s\n' % func.__name__2461        func()24622463    ignore = multiprocessing.active_children()      # очистка всех старых процессов2464    if hasattr(multiprocessing, '_debug_info'):2465        info = multiprocessing._debug_info()2466        if info:2467            print info2468            raise ValueError('there should be no positive refcounts left')24692470if __name__ == '__main__':2471    multiprocessing.freeze_support()24722473    assert len(sys.argv) in (1, 2)24742475    if len(sys.argv) == 1 or sys.argv[1] == 'processes':2476        print ' Using processes '.center(79, '-')2477        namespace = multiprocessing2478    elif sys.argv[1] == 'manager':2479        print ' Using processes and a manager '.center(79, '-')2480        namespace = multiprocessing.Manager()2481        namespace.Process = multiprocessing.Process2482        namespace.current_process = multiprocessing.current_process2483        namespace.active_children = multiprocessing.active_children2484    elif sys.argv[1] == 'threads':2485        print ' Using threads '.center(79, '-')2486        import multiprocessing.dummy as namespace2487    else:2488        print 'Usage:\n\t%s [processes | manager | threads]' % sys.argv[0]2489        raise SystemExit(2)24902491    test(namespace)2492```24932494Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:24952496```python2497#2498# Простой пример использования пула рабочих процессов для выполнения задач.2499#2500# Обратите внимание, что результаты, скорее всего, не будут получены из выходной2501# очереди в том же порядке, в котором соответствующие задачи были2502# помещены во входную очередь. Если важно получить результаты2503# в исходном порядке, рассмотрите использование `Pool.map()` или2504# `Pool.imap()` (что в любом случае сократит объём необходимого кода).2505#2506# Авторское право (c) 2006-2008, R Oudkerk2507# Все права защищены.2508#25092510import time2511import random25122513from multiprocessing import Process, Queue, current_process, freeze_support25142515#2516# Функция, выполняемая рабочими процессами2517#25182519def worker(input, output):2520    for func, args in iter(input.get, 'STOP'):2521        result = calculate(func, args)2522        output.put(result)25232524#2525# Функция, используемая для вычисления результата2526#25272528def calculate(func, args):2529    result = func(*args)2530    return '%s says that %s%s = %s' % \2531        (current_process().name, func.__name__, args, result)25322533#2534# Функции, на которые ссылаются задачи2535#25362537def mul(a, b):2538    time.sleep(0.5*random.random())2539    return a * b25402541def plus(a, b):2542    time.sleep(0.5*random.random())2543    return a + b25442545#2546#2547#25482549def test():2550    NUMBER_OF_PROCESSES = 42551    TASKS1 = [(mul, (i, 7)) for i in range(20)]2552    TASKS2 = [(plus, (i, 8)) for i in range(10)]25532554    # Создание очередей2555    task_queue = Queue()2556    done_queue = Queue()25572558    # Отправка задач2559    for task in TASKS1:2560        task_queue.put(task)25612562    # Запуск рабочих процессов2563    for i in range(NUMBER_OF_PROCESSES):2564        Process(target=worker, args=(task_queue, done_queue)).start()25652566    # Получение и вывод результатов2567    print 'Unordered results:'2568    for i in range(len(TASKS1)):2569        print '\t', done_queue.get()25702571    # Добавить ещё задач с помощью `put()`2572    for task in TASKS2:2573        task_queue.put(task)25742575    # Получить и вывести ещё несколько результатов2576    for i in range(len(TASKS2)):2577        print '\t', done_queue.get()25782579    # Сообщить дочерним процессам об остановке2580    for i in range(NUMBER_OF_PROCESSES):2581        task_queue.put('STOP')25822583if __name__ == '__main__':2584    freeze_support()2585    test()2586```25872588Пример того, как пул рабочих процессов может запускать экземпляр `SimpleHTTPServer.HttpServer`, используя один общий прослушивающий сокет.25892590```python2591#2592# Пример, в котором пул HTTP-серверов использует один общий слушающий сокет2593#2594# В Windows этот модуль зависит от возможности сериализации сокета с помощью pickle2595# объекта, чтобы рабочие процессы могли унаследовать копию сервера2596# объект. (Мы импортируем `multiprocessing.reduction`, чтобы включить эту сериализацию.)2597#2598# Не уверены, нужно ли синхронизировать доступ к методу `socket.accept()` с помощью2599# блокировки, разделяемой между процессами – похоже, это не обязательно.2600#2601# Авторское право (c) 2006-2008, R Oudkerk2602# Все права защищены.2603#26042605import os2606import sys26072608from multiprocessing import Process, current_process, freeze_support2609from BaseHTTPServer import HTTPServer2610from SimpleHTTPServer import SimpleHTTPRequestHandler26112612if sys.platform == 'win32':2613    import multiprocessing.reduction    # сделать сокеты сериализуемыми/наследуемыми26142615def note(format, *args):2616    sys.stderr.write('[%s]\t%s\n' % (current_process().name, format%args))26172618class RequestHandler(SimpleHTTPRequestHandler):2619    # мы переопределяем log_message(), чтобы показывать, какой процесс обрабатывает запрос2620    def log_message(self, format, *args):2621        note(format, *args)26222623def serve_forever(server):2624    note('starting server')2625    try:2626        server.serve_forever()2627    except KeyboardInterrupt:2628        pass26292630def runpool(address, number_of_processes):2631    # создать единственный объект сервера – каждый дочерний процесс унаследует его копию2632    server = HTTPServer(address, RequestHandler)26332634    # создать дочерние процессы, которые будут работать как воркеры2635    for i in range(number_of_processes-1):2636        Process(target=serve_forever, args=(server,)).start()26372638    # главный процесс также выступает в роли воркера2639    serve_forever(server)26402641def test():2642    DIR = os.path.join(os.path.dirname(__file__), '..')2643    ADDRESS = ('localhost', 8000)2644    NUMBER_OF_PROCESSES = 426452646    print 'Serving at http://%s:%d using %d worker processes' % \2647          (ADDRESS[0], ADDRESS[1], NUMBER_OF_PROCESSES)2648    print 'To exit press Ctrl-' + ['C', 'Break'][sys.platform=='win32']26492650    os.chdir(DIR)2651    runpool(ADDRESS, NUMBER_OF_PROCESSES)26522653if __name__ == '__main__':2654    freeze_support()2655    test()2656```26572658Несколько простых тестов, сравнивающих [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) с [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading):26592660```python2661#2662# Простые тесты производительности для пакета multiprocessing2663#2664# Авторское право (c) 2006-2008, R Oudkerk2665# Все права защищены.2666#26672668import time, sys, multiprocessing, threading, Queue, gc26692670if sys.platform == 'win32':2671    _timer = time.clock2672else:2673    _timer = time.time26742675delta = 126762677#### TEST_QUEUESPEED26782679def queuespeed_func(q, c, iterations):2680    a = '0' * 2562681    c.acquire()2682    c.notify()2683    c.release()26842685    for i in xrange(iterations):2686        q.put(a)26872688    q.put('STOP')26892690def test_queuespeed(Process, q, c):2691    elapsed = 02692    iterations = 126932694    while elapsed < delta:2695        iterations *= 226962697        p = Process(target=queuespeed_func, args=(q, c, iterations))2698        c.acquire()2699        p.start()2700        c.wait()2701        c.release()27022703        result = None2704        t = _timer()27052706        while result != 'STOP':2707            result = q.get()27082709        elapsed = _timer() - t27102711        p.join()27122713    print iterations, 'objects passed through the queue in', elapsed, 'seconds'2714    print 'average number/sec:', iterations/elapsed27152716#### TEST_PIPESPEED27172718def pipe_func(c, cond, iterations):2719    a = '0' * 2562720    cond.acquire()2721    cond.notify()2722    cond.release()27232724    for i in xrange(iterations):2725        c.send(a)27262727    c.send('STOP')27282729def test_pipespeed():2730    c, d = multiprocessing.Pipe()2731    cond = multiprocessing.Condition()2732    elapsed = 02733    iterations = 127342735    while elapsed < delta:2736        iterations *= 227372738        p = multiprocessing.Process(target=pipe_func,2739                                    args=(d, cond, iterations))2740        cond.acquire()2741        p.start()2742        cond.wait()2743        cond.release()27442745        result = None2746        t = _timer()27472748        while result != 'STOP':2749            result = c.recv()27502751        elapsed = _timer() - t2752        p.join()27532754    print iterations, 'objects passed through connection in',elapsed,'seconds'2755    print 'average number/sec:', iterations/elapsed27562757#### TEST_SEQSPEED27582759def test_seqspeed(seq):2760    elapsed = 02761    iterations = 127622763    while elapsed < delta:2764        iterations *= 227652766        t = _timer()27672768        for i in xrange(iterations):2769            a = seq[5]27702771        elapsed = _timer()-t27722773    print iterations, 'iterations in', elapsed, 'seconds'2774    print 'average number/sec:', iterations/elapsed27752776#### TEST_LOCK27772778def test_lockspeed(l):2779    elapsed = 02780    iterations = 127812782    while elapsed < delta:2783        iterations *= 227842785        t = _timer()27862787        for i in xrange(iterations):2788            l.acquire()2789            l.release()27902791        elapsed = _timer()-t27922793    print iterations, 'iterations in', elapsed, 'seconds'2794    print 'average number/sec:', iterations/elapsed27952796#### TEST_CONDITION27972798def conditionspeed_func(c, N):2799    c.acquire()2800    c.notify()28012802    for i in xrange(N):2803        c.wait()2804        c.notify()28052806    c.release()28072808def test_conditionspeed(Process, c):2809    elapsed = 02810    iterations = 128112812    while elapsed < delta:2813        iterations *= 228142815        c.acquire()2816        p = Process(target=conditionspeed_func, args=(c, iterations))2817        p.start()28182819        c.wait()28202821        t = _timer()28222823        for i in xrange(iterations):2824            c.notify()2825            c.wait()28262827        elapsed = _timer()-t28282829        c.release()2830        p.join()28312832    print iterations * 2, 'waits in', elapsed, 'seconds'2833    print 'average number/sec:', iterations * 2 / elapsed28342835####28362837def test():2838    manager = multiprocessing.Manager()28392840    gc.disable()28412842    print '\n\t######## testing Queue.Queue\n'2843    test_queuespeed(threading.Thread, Queue.Queue(),2844                    threading.Condition())2845    print '\n\t######## testing multiprocessing.Queue\n'2846    test_queuespeed(multiprocessing.Process, multiprocessing.Queue(),2847                    multiprocessing.Condition())2848    print '\n\t######## testing Queue managed by server process\n'2849    test_queuespeed(multiprocessing.Process, manager.Queue(),2850                    manager.Condition())2851    print '\n\t######## testing multiprocessing.Pipe\n'2852    test_pipespeed()28532854    print28552856    print '\n\t######## testing list\n'2857    test_seqspeed(range(10))2858    print '\n\t######## testing list managed by server process\n'2859    test_seqspeed(manager.list(range(10)))2860    print '\n\t######## testing Array("i", ..., lock=False)\n'2861    test_seqspeed(multiprocessing.Array('i', range(10), lock=False))2862    print '\n\t######## testing Array("i", ..., lock=True)\n'2863    test_seqspeed(multiprocessing.Array('i', range(10), lock=True))28642865    print28662867    print '\n\t######## testing threading.Lock\n'2868    test_lockspeed(threading.Lock())2869    print '\n\t######## testing threading.RLock\n'2870    test_lockspeed(threading.RLock())2871    print '\n\t######## testing multiprocessing.Lock\n'2872    test_lockspeed(multiprocessing.Lock())2873    print '\n\t######## testing multiprocessing.RLock\n'2874    test_lockspeed(multiprocessing.RLock())2875    print '\n\t######## testing lock managed by server process\n'2876    test_lockspeed(manager.Lock())2877    print '\n\t######## testing rlock managed by server process\n'2878    test_lockspeed(manager.RLock())28792880    print28812882    print '\n\t######## testing threading.Condition\n'2883    test_conditionspeed(threading.Thread, threading.Condition())2884    print '\n\t######## testing multiprocessing.Condition\n'2885    test_conditionspeed(multiprocessing.Process, multiprocessing.Condition())2886    print '\n\t######## testing condition managed by a server process\n'2887    test_conditionspeed(multiprocessing.Process, manager.Condition())28882889    gc.enable()28902891if __name__ == '__main__':2892    multiprocessing.freeze_support()2893    test()2894```2895