> **Источник:** https://python-all.ru/2.7/library/multiprocessing.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# 16.6. [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) – интерфейс «threading» на основе процессов

Новое в версии 2.6.

## 16.6.1. Введение

[`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.

Модуль [`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`,

```python
from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    p = Pool(5)
    print(p.map(f, [1, 2, 3]))
```

будет выводить на стандартный вывод

```python
[1, 4, 9]
```

### 16.6.1.1. Класс [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process)

В [`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). Простой пример многопроцессной программы:

```python
from multiprocessing import Process

def f(name):
    print 'hello', name

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
```

Чтобы показать идентификаторы задействованных процессов, вот расширенный пример:

```python
from multiprocessing import Process
import os

def info(title):
    print title
    print 'module name:', __name__
    if hasattr(os, 'getppid'):  # доступно только на Unix
        print 'parent process:', os.getppid()
    print 'process id:', os.getpid()

def f(name):
    info('function f')
    print 'hello', name

if __name__ == '__main__':
    info('main line')
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()
```

Объяснение того, почему (в Windows) часть `if __name__ == '__main__'` необходима, см. в [рекомендациях по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming).

### 16.6.1.2. Обмен объектами между процессами

[`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) поддерживает два типа каналов связи между процессами:

**Очереди**

> Класс [`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). Например:
>
> ```python
> from multiprocessing import Process, Queue
>
> def f(q):
>     q.put([42, None, 'hello'])
>
> if __name__ == '__main__':
>     q = Queue()
>     p = Process(target=f, args=(q,))
>     p.start()
>     print q.get()    # печатает "[42, None, 'hello']"
>     p.join()
> ```
>
> Очереди потокобезопасны и безопасны для процессов.

**Каналы**

> Функция [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) возвращает пару объектов соединения, связанных каналом, который по умолчанию является дуплексным (двунаправленным). Например:
>
> ```python
> from multiprocessing import Process, Pipe
>
> def f(conn):
>     conn.send([42, None, 'hello'])
>     conn.close()
>
> if __name__ == '__main__':
>     parent_conn, child_conn = Pipe()
>     p = Process(target=f, args=(child_conn,))
>     p.start()
>     print parent_conn.recv()   # печатает "[42, None, 'hello']"
>     p.join()
> ```
>
> Два объекта соединения, возвращаемые [`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) (среди прочих). Обратите внимание: данные в канале могут быть повреждены, если два процесса (или потока) попытаются одновременно читать или писать в *один и тот же* конец канала. Разумеется, нет риска повреждения, если процессы одновременно используют разные концы канала.

### 16.6.1.3. Синхронизация между процессами

[`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) содержит эквиваленты всех примитивов синхронизации из [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading). Например, можно использовать блокировку, чтобы гарантировать, что только один процесс одновременно выводит на стандартный вывод:

```python
from multiprocessing import Process, Lock

def f(l, i):
    l.acquire()
    print 'hello world', i
    l.release()

if __name__ == '__main__':
    lock = Lock()

    for num in range(10):
        Process(target=f, args=(lock, num)).start()
```

Без использования блокировки вывод от разных процессов может полностью перепутаться.

### 16.6.1.4. Обмен состоянием между процессами

Как уже упоминалось, при параллельном программировании обычно лучше избегать использования общего состояния, насколько это возможно. Это особенно верно при использовании нескольких процессов.

Однако если действительно необходимо использовать некоторые общие данные, то [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) предоставляет несколько способов сделать это.

**Разделяемая память**

> Данные могут храниться в отображении разделяемой памяти с помощью [`Value`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Value) или [`Array`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Array). Например, следующий код
>
> ```python
> from multiprocessing import Process, Value, Array
>
> def f(n, a):
>     n.value = 3.1415927
>     for i in range(len(a)):
>         a[i] = -a[i]
>
> if __name__ == '__main__':
>     num = Value('d', 0.0)
>     arr = Array('i', range(10))
>
>     p = Process(target=f, args=(num, arr))
>     p.start()
>     p.join()
>
>     print num.value
>     print arr[:]
> ```
>
> выведет
>
> ```python
> 3.1415927
> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
> ```
>
> Аргументы `'d'` и `'i'`, используемые при создании `num` и `arr`, – это коды типов, аналогичные используемым модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array): `'d'` означает число с плавающей запятой двойной точности, а `'i'` – целое число со знаком. Эти разделяемые объекты будут безопасными для процессов и потоков.
>
> Для большей гибкости при работе с разделяемой памятью можно использовать модуль [`multiprocessing.sharedctypes`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.sharedctypes), который поддерживает создание произвольных объектов ctypes, выделенных из разделяемой памяти.

**Серверный процесс**

> Объект-менеджер, возвращаемый `Manager()`, управляет серверным процессом, который содержит объекты Python и позволяет другим процессам манипулировать ими с помощью прокси.
>
> Менеджер, возвращаемый `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). Например,
>
> ```python
> from multiprocessing import Process, Manager
>
> def f(d, l):
>     d[1] = '1'
>     d['2'] = 2
>     d[0.25] = None
>     l.reverse()
>
> if __name__ == '__main__':
>     manager = Manager()
>
>     d = manager.dict()
>     l = manager.list(range(10))
>
>     p = Process(target=f, args=(d, l))
>     p.start()
>     p.join()
>
>     print d
>     print l
> ```
>
> выведет
>
> ```python
> {0.25: None, 1: '1', '2': 2}
> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
> ```
>
> Серверные менеджеры процессов более гибкие, чем использование объектов разделяемой памяти, потому что их можно настроить на поддержку произвольных типов объектов. Кроме того, один менеджер может быть разделён между процессами на разных компьютерах через сеть. Однако они медленнее, чем использование разделяемой памяти.

### 16.6.1.5. Использование пула процессов

Класс `Pool` представляет пул рабочих процессов. Он имеет методы, которые позволяют передавать задачи рабочим процессам несколькими различными способами.

Например:

```python
from multiprocessing import Pool, TimeoutError
import time
import os

def f(x):
    return x*x

if __name__ == '__main__':
    pool = Pool(processes=4)              # запустить 4 рабочих процесса

    # печатает "[0, 1, 4,..., 81]"
    print pool.map(f, range(10))

    # печатает те же числа в произвольном порядке
    for i in pool.imap_unordered(f, range(10)):
        print i

    # вычисляет "f(20)" асинхронно
    res = pool.apply_async(f, (20,))      # выполняется *только* в одном процессе
    print res.get(timeout=1)              # печатает "400"

    # вычисляет "os.getpid()" асинхронно
    res = pool.apply_async(os.getpid, ()) # выполняется *только* в одном процессе
    print res.get(timeout=1)              # печатает PID этого процесса

    # запуск нескольких асинхронных вычислений *может* задействовать больше процессов
    multiple_results = [pool.apply_async(os.getpid, ()) for i in range(4)]
    print [res.get(timeout=1) for res in multiple_results]

    # заставить один рабочий процесс спать 10 секунд
    res = pool.apply_async(time.sleep, (10,))
    try:
        print res.get(timeout=1)
    except TimeoutError:
        print "We lacked patience and got a multiprocessing.TimeoutError"
```

Обратите внимание: методы пула должны использоваться только тем процессом, который его создал.

> **Примечание**
>
> Для работы функциональности этого пакета требуется, чтобы модуль `__main__` был доступен для импорта дочерними процессами. Это рассматривается в [рекомендациях по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming), но здесь стоит упомянуть об этом. Это означает, что некоторые примеры, такие как примеры с `Pool`, не будут работать в интерактивном интерпретаторе. Например:
>
> ```python
> >>> from multiprocessing import Pool
> >>> p = Pool(5)
> >>> def f(x):
> ...     return x*x
> ...
> >>> p.map(f, [1,2,3])
> Process PoolWorker-1:
> Process PoolWorker-2:
> Process PoolWorker-3:
> Traceback (most recent call last):
> AttributeError: 'module' object has no attribute 'f'
> AttributeError: 'module' object has no attribute 'f'
> AttributeError: 'module' object has no attribute 'f'
> ```
>
> (Если попробовать это сделать, на самом деле будут выведены три полные трассировки стека, перемешанные в полу-случайном порядке, и затем, возможно, придётся как-то остановить главный процесс.)

## 16.6.2. Справочник

Пакет [`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).

### 16.6.2.1. [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process) и исключения

#### `class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})`

Объекты 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).

Конструктор всегда следует вызывать с именованными аргументами. *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*.

Если подкласс переопределяет конструктор, он должен вызывать конструктор базового класса (`Process.__init__()`) перед любыми другими действиями над процессом.

#### `run()`

Метод, представляющий активность процесса.

Этот метод можно переопределить в подклассе. Стандартный метод [`run()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.run) вызывает вызываемый объект, переданный конструктору объекта в качестве аргумента target, если он есть, с позиционными и именованными аргументами, взятыми из аргументов *args* и *kwargs* соответственно.

#### `start()`

Запускает активность процесса.

Этот метод должен быть вызван не более одного раза для каждого объекта процесса. Он обеспечивает вызов метода [`run()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.run) объекта в отдельном процессе.

#### `join([timeout])`

Блокирует вызывающий поток до завершения процесса, чей метод [`join()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.join) был вызван, или до истечения указанного таймаута.

Если *timeout* равен `None`, таймаут отсутствует.

Процесс может быть присоединён много раз.

Процесс не может присоединить сам себя, так как это приведёт к взаимоблокировке. Попытка вызвать join для процесса до его запуска является ошибкой.

#### `name`

Имя процесса.

Имя – это строка, используемая только для идентификации. Она не несет семантической нагрузки. Несколько процессов могут иметь одинаковое имя. Начальное имя задается конструктором.

#### `is_alive()`

Возвращает, жив ли процесс.

Грубо говоря, объект процесса живёт с момента возврата метода [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start) до завершения дочернего процесса.

#### `daemon`

Флаг демона процесса (логическое значение). Должен быть установлен до вызова [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start).

Начальное значение наследуется от создающего процесса.

Когда процесс завершается, он пытается завершить все свои дочерние процессы-демоны.

Обратите внимание, что процессу-демону не разрешается создавать дочерние процессы. Иначе процесс-демон оставил бы своих потомков осиротевшими, если бы он был завершён при выходе своего родительского процесса. Кроме того, это **не** демоны или службы Unix, а обычные процессы, которые будут завершены (и не присоединены), если не-демонические процессы завершились.

В дополнение к 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) также поддерживают следующие атрибуты и методы:

#### `pid`

Возвращает идентификатор процесса. До запуска процесса это будет `None`.

#### `exitcode`

Код завершения дочернего процесса. Он будет равен `None`, если процесс ещё не завершён. Отрицательное значение *-N* означает, что дочерний процесс был завершён сигналом *N*.

#### `authkey`

Ключ аутентификации процесса (байтовая строка).

При инициализации [`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).

При создании объекта [`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) в другую байтовую строку.

См. [Ключи аутентификации](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-auth-keys).

#### `terminate()`

Завершает процесс. На Unix это делается с помощью сигнала `SIGTERM`; на Windows используется `TerminateProcess()`. Обратите внимание, что обработчики выхода и блоки finally и т.д. выполняться не будут.

Обратите внимание, что процессы-потомки *не* будут завершены – они просто станут осиротевшими.

> **Предупреждение**
>
> Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и стать непригодными для использования другими процессами. Аналогично, если процесс захватил блокировку или семафор и т.п., то его завершение может привести к взаимной блокировке других процессов.

Обратите внимание: методы [`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) должны вызываться только тем процессом, который создал объект процесса.

Пример использования некоторых методов [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process):

```pycon
>>> import multiprocessing, time, signal
>>> p = multiprocessing.Process(target=time.sleep, args=(1000,))
>>> print p, p.is_alive()
<Process(Process-1, initial)> False
>>> p.start()
>>> print p, p.is_alive()
<Process(Process-1, started)> True
>>> p.terminate()
>>> time.sleep(0.1)
>>> print p, p.is_alive()
<Process(Process-1, stopped[SIGTERM])> False
>>> p.exitcode == -signal.SIGTERM
True
```

#### `exception multiprocessing.BufferTooShort`

Исключение, возбуждаемое [`Connection.recv_bytes_into()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv_bytes_into), когда предоставленный буфер слишком мал для чтения сообщения.

Если `e` является экземпляром [`BufferTooShort`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.BufferTooShort), то `e.args[0]` вернёт сообщение в виде байтовой строки.

### 16.6.2.2. Каналы и очереди

При использовании нескольких процессов обычно применяется передача сообщений для взаимодействия между процессами, что позволяет избежать использования примитивов синхронизации, таких как блокировки.

Для передачи сообщений можно использовать [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) (для соединения между двумя процессами) или очередь (которая допускает несколько производителей и потребителей).

Типы [`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.

Если используется [`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) для каждой задачи, извлечённой из очереди; в противном случае семафор, используемый для подсчёта незавершённых задач, может переполниться, что приведёт к возбуждению исключения.

Обратите внимание, что общую очередь также можно создать с помощью объекта-менеджера – см. [Менеджеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).

> **Примечание**
>
> [`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).

> **Примечание**
>
> Когда объект помещается в очередь, он сериализуется с помощью модуля pickle, и фоновый поток позднее сбрасывает сериализованные данные в нижележащий канал. Это имеет некоторые неожиданные последствия, но обычно не вызывает практических трудностей – если это действительно беспокоит, можно вместо этого использовать очередь, созданную с помощью [менеджера](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).
>
> 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).
> 2. Если несколько процессов помещают объекты в очередь, возможно получение объектов на другом конце не по порядку. Однако объекты, помещённые в очередь одним и тем же процессом, всегда будут идти друг за другом в ожидаемом порядке.

> **Предупреждение**
>
> Если процесс завершается принудительно с помощью [`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), данные в очереди могут быть повреждены. Это может привести к тому, что любой другой процесс при попытке использовать очередь получит исключение.

> **Предупреждение**
>
> Как упоминалось выше, если дочерний процесс поместил элементы в очередь (и не использовал [`JoinableQueue.cancel_join_thread`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread)), он не завершится, пока все буферизованные элементы не будут сброшены в канал.
>
> Это означает, что при попытке присоединиться к этому процессу может возникнуть взаимоблокировка, если не быть уверенным, что все помещённые в очередь элементы были потреблены. Аналогично, если дочерний процесс не является демоническим, родительский процесс может зависнуть при завершении, когда попытается присоединиться ко всем своим недемоническим потомкам.
>
> Обратите внимание, что очередь, созданная с помощью менеджера, не имеет этой проблемы. См. [Рекомендации по программированию](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-programming).

Пример использования очередей для межпроцессного взаимодействия см. в разделе [Примеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-examples).

#### `multiprocessing.Pipe([duplex])`

Возвращает пару `(conn1, conn2)` объектов [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection), представляющих концы канала.

Если *duplex* равно `True` (по умолчанию), то канал двунаправленный. Если *duplex* равно `False`, то канал однонаправленный: `conn1` можно использовать только для получения сообщений, а `conn2` – только для отправки.

#### `class multiprocessing.Queue([maxsize])`

Возвращает общедоступную очередь для процессов, реализованную с помощью канала и нескольких блокировок/семафоров. Когда процесс впервые помещает элемент в очередь, запускается поток-загрузчик, который передаёт объекты из буфера в канал.

Обычные исключения [`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) стандартной библиотеки возбуждаются для сигнализации тайм-аутов.

[`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).

#### `qsize()`

Возвращает приблизительный размер очереди. Из-за семантики многопоточности/многопроцессности это число не является надёжным.

Обратите внимание, что это может вызвать [`NotImplementedError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.NotImplementedError) на Unix-платформах, таких как Mac OS X, где `sem_getvalue()` не реализован.

#### `empty()`

Возвращает `True`, если очередь пуста, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.

#### `full()`

Возвращает `True`, если очередь заполнена, иначе `False`. Из-за семантики многопоточности/многопроцессности это ненадёжно.

#### `put(obj[, block[, timeout]])`

Помещает 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* в этом случае игнорируется).

#### `put_nowait(obj)`

Эквивалентно `put(obj, False)`.

#### `get([block[, timeout]])`

Удаляет и возвращает элемент из очереди. Если необязательный аргумент *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* в этом случае игнорируется).

#### `get_nowait()`

Эквивалентно `get(False)`.

[`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). Обычно эти методы не нужны в большинстве кода:

#### `close()`

Указывает, что текущий процесс больше не будет помещать данные в эту очередь. Фоновый поток завершится после того, как сбросит все буферизованные данные в канал. Этот метод вызывается автоматически при сборке мусора очереди.

#### `join_thread()`

Присоединяет фоновый поток. Этот метод можно использовать только после вызова [`close()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.close). Он блокируется до завершения фонового потока, гарантируя, что все данные из буфера сброшены в канал.

По умолчанию, если процесс не является создателем очереди, при завершении он попытается присоединить фоновый поток очереди. Процесс может вызвать [`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) бездействующим.

#### `cancel_join_thread()`

Предотвращает блокировку [`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).

Более подходящим именем для этого метода могло бы быть `allow_exit_without_flush()`. Он, скорее всего, приведёт к потере поставленных в очередь данных, и почти наверняка вам не понадобится его использовать. Он нужен только в том случае, если текущий процесс должен немедленно завершиться, не дожидаясь сброса поставленных в очередь данных в нижележащий канал, и вас не волнует потеря данных.

> **Примечание**
>
> Функциональность этого класса требует работающей реализации общего семафора в хост-операционной системе. Без неё функциональность класса будет отключена, а попытки создать экземпляр [`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) для дополнительной информации. То же самое относится и ко всем специализированным типам очередей, перечисленным ниже.

#### `class multiprocessing.queues.SimpleQueue`

Это упрощённый тип [`Queue`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue), очень похожий на [`Pipe`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) с блокировкой.

#### `empty()`

Возвращает `True`, если очередь пуста, иначе `False`.

#### `get()`

Извлекает и возвращает элемент из очереди.

#### `put(item)`

Помещает *item* в очередь.

#### `class multiprocessing.JoinableQueue([maxsize])`

[`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).

#### `task_done()`

Указывает, что ранее поставленная в очередь задача завершена. Используется потоками-потребителями очереди. Для каждого вызова [`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) сообщает очереди, что обработка задачи завершена.

Если [`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)).

Вызывает [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError), если вызывается больше раз, чем было помещено элементов в очередь.

#### `join()`

Блокируется до тех пор, пока все элементы в очереди не будут извлечены и обработаны.

Счётчик незавершённых задач увеличивается при каждом добавлении элемента в очередь. Счётчик уменьшается, когда поток-потребитель вызывает [`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) разблокируется.

### 16.6.2.3. Разное

#### `multiprocessing.active_children()`

Возвращает список всех активных дочерних процессов текущего процесса.

Вызов этой функции имеет побочный эффект – «присоединение» всех уже завершённых процессов.

#### `multiprocessing.cpu_count()`

Возвращает количество процессоров в системе. Может вызывать [`NotImplementedError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.NotImplementedError).

#### `multiprocessing.current_process()`

Возвращает объект [`Process`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process), соответствующий текущему процессу.

Аналог [`threading.current_thread()`](https://python-all.ru/2.7/library/threading.html#threading.current_thread).

#### `multiprocessing.freeze_support()`

Добавляет поддержку для случая, когда программа, использующая [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing), была заморожена для создания исполняемого файла Windows. (Протестировано с **py2exe**, **PyInstaller** и **cx\_Freeze**.)

Эту функцию необходимо вызвать сразу после строки `if __name__ == '__main__'` в главном модуле. Например:

```python
from multiprocessing import Process, freeze_support

def f():
    print 'hello world!'

if __name__ == '__main__':
    freeze_support()
    Process(target=f).start()
```

Если строка `freeze_support()` опущена, то попытка запустить замороженный исполняемый файл вызовет [`RuntimeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.RuntimeError).

Вызов `freeze_support()` не имеет эффекта при запуске на любой операционной системе, кроме Windows. Кроме того, если модуль запускается обычным образом интерпретатором Python в Windows (программа не была заморожена), то `freeze_support()` также не имеет эффекта.

#### `multiprocessing.set_executable()`

Устанавливает путь к интерпретатору Python, используемому при запуске дочернего процесса. (По умолчанию используется [`sys.executable`](https://python-all.ru/2.7/library/sys.html#sys.executable)). Встраивающим пользователям, вероятно, потребуется сделать что-то вроде

```python
set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))
```

прежде чем они смогут создавать дочерние процессы. (Только для Windows)

> **Примечание**
>
> [`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).

### 16.6.2.4. Объекты соединений

Объекты подключения позволяют отправлять и получать сериализуемые (picklable) объекты или строки. Их можно рассматривать как ориентированные на сообщения соединённые сокеты.

Объекты подключения обычно создаются с помощью [`Pipe`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe) – см. также [Слушатели и клиенты](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-listeners-clients).

#### `class Connection`

#### `send(obj)`

Отправляет объект на другой конец подключения; его следует читать с помощью [`recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv).

Объект должен быть сериализуемым. Очень большие сериализованные объекты (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError).

#### `recv()`

Возвращает объект, отправленный с другого конца подключения с помощью [`send()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.send). Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего получать и другой конец был закрыт.

#### `fileno()`

Возвращает файловый дескриптор или дескриптор (handle), используемый подключением.

#### `close()`

Закрывает подключение.

Вызывается автоматически при сборке мусора для подключения.

#### `poll([timeout])`

Возвращает, есть ли данные для чтения.

Если *timeout* не указан, то метод возвращается немедленно. Если *timeout* – число, то оно задаёт максимальное время блокировки в секундах. Если *timeout* равно `None`, то используется бесконечное ожидание.

#### `send_bytes(buffer[, offset[, size]])`

Отправляет байтовые данные из объекта, поддерживающего интерфейс буфера, в виде целого сообщения.

Если указан *offset*, то данные читаются с этой позиции в *buffer*. Если указан *size*, то из буфера будет прочитано соответствующее количество байт. Очень большие буферы (примерно 32 МБ+, хотя это зависит от ОС) могут вызвать исключение [`ValueError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.ValueError)

#### `recv_bytes([maxlength])`

Возвращает полное сообщение из байтовых данных, отправленное с другого конца подключения, в виде строки. Блокируется, пока не появятся данные для получения. Вызывает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего получать и другой конец закрыт.

Если указан *maxlength* и сообщение длиннее *maxlength*, то вызывается [`IOError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.IOError), и подключение больше не будет доступно для чтения.

#### `recv_bytes_into(buffer[, offset])`

Читает в *buffer* полное сообщение с байтовыми данными, отправленное с другого конца соединения, и возвращает количество байтов в сообщении. Блокирует выполнение, пока не появится что-то для приёма. Возбуждает [`EOFError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.EOFError), если больше нечего принимать и другой конец был закрыт.

*buffer* должен быть объектом, удовлетворяющим интерфейсу буфера для записи. Если задан *offset*, сообщение будет записано в буфер, начиная с этой позиции. Значение offset должно быть неотрицательным целым числом, меньшим длины *buffer* (в байтах).

Если буфер слишком мал, то вызывается исключение `BufferTooShort` и полное сообщение доступно как `e.args[0]`, где `e` экземпляр исключения.

Например:

```pycon
>>> from multiprocessing import Pipe
>>> a, b = Pipe()
>>> a.send([1, 'hello', None])
>>> b.recv()
[1, 'hello', None]
>>> b.send_bytes('thank you')
>>> a.recv_bytes()
'thank you'
>>> import array
>>> arr1 = array.array('i', range(5))
>>> arr2 = array.array('i', [0] * 10)
>>> a.send_bytes(arr1)
>>> count = b.recv_bytes_into(arr2)
>>> assert count == len(arr1) * arr1.itemsize
>>> arr2
array('i', [0, 1, 2, 3, 4, 0, 0, 0, 0, 0])
```

> **Предупреждение**
>
> Метод [`Connection.recv()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv) автоматически десериализует полученные данные, что может быть угрозой безопасности, если нельзя доверять процессу, отправившему сообщение.
>
> Поэтому, если объект подключения не был создан с помощью `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).

> **Предупреждение**
>
> Если процесс завершается принудительно во время чтения или записи в канал, данные в канале, скорее всего, повредятся, так как может стать невозможно определить границы сообщений.

### 16.6.2.5. Примитивы синхронизации

В целом, в многопроцессных программах примитивы синхронизации требуются не так часто, как в многопоточных. См. документацию модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).

Обратите внимание, что примитивы синхронизации можно также создавать с помощью объекта менеджера – см. [Менеджеры](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-managers).

#### `class multiprocessing.BoundedSemaphore([value])`

Объект ограниченного семафора: близкий аналог [`threading.BoundedSemaphore`](https://python-all.ru/2.7/library/threading.html#threading.BoundedSemaphore).

Единственное отличие от его близкого аналога: первый аргумент метода `acquire` называется *block*, и он поддерживает необязательный второй аргумент *timeout*, что согласуется с [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire).

> **Примечание**
>
> На Mac OS X это неотличимо от [`Semaphore`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Semaphore), поскольку `sem_getvalue()` не реализован на этой платформе.

#### `class multiprocessing.Condition([lock])`

Условная переменная: клон [`threading.Condition`](https://python-all.ru/2.7/library/threading.html#threading.Condition).

Если указана *блокировка*, то она должна быть объектом [`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).

#### `class multiprocessing.Event`

Клон [`threading.Event`](https://python-all.ru/2.7/library/threading.html#threading.Event). Этот метод возвращает состояние внутреннего семафора при выходе, поэтому он всегда возвращает `True`, за исключением случаев, когда задан таймаут и операция истекает по таймауту.

Изменено в версии 2.7: Ранее метод всегда возвращал `None`.

#### `class multiprocessing.Lock`

Нерекурсивный объект блокировки: близкий аналог [`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) применительно к процессам или потокам, за исключением отмеченных случаев.

Обратите внимание, что [`Lock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.Lock`, инициализированный контекстом по умолчанию.

[`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).

#### `acquire(block=True, timeout=None)`

Захватывает блокировку, блокирующую или неблокирующую.

Если аргумент *block* установлен в `True` (по умолчанию), вызов метода будет блокироваться, пока блокировка не окажется в разблокированном состоянии, затем установит её в заблокированное состояние и вернёт `True`. Обратите внимание, что имя этого первого аргумента отличается от имени в [`threading.Lock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.Lock.acquire).

Если аргумент *block* установлен в `False`, вызов метода не блокируется. Если блокировка в данный момент находится в заблокированном состоянии, возвращается `False`; в противном случае блокировка устанавливается в заблокированное состояние и возвращается `True`.

При вызове с положительным значением с плавающей точкой для *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).

#### `release()`

Освобождает блокировку. Может вызываться из любого процесса или потока, а не только из того, который изначально захватил блокировку.

Поведение аналогично [`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).

#### `class multiprocessing.RLock`

Объект рекурсивной блокировки: близкий аналог [`threading.RLock`](https://python-all.ru/2.7/library/threading.html#threading.RLock). Рекурсивная блокировка должна быть освобождена тем же процессом или потоком, который её захватил. После того как процесс или поток захватил рекурсивную блокировку, тот же процесс или поток может захватить её снова без блокировки; этот процесс или поток должен освободить её по одному разу за каждый захват.

Обратите внимание, что [`RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) на самом деле является фабричной функцией, которая возвращает экземпляр `multiprocessing.synchronize.RLock`, инициализированный контекстом по умолчанию.

[`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).

#### `acquire(block=True, timeout=None)`

Захватывает блокировку, блокирующую или неблокирующую.

При вызове с аргументом *block*, установленным в `True`, блокируется до тех пор, пока блокировка не окажется в разблокированном состоянии (не принадлежит ни одному процессу или потоку), если только блокировка уже не принадлежит текущему процессу или потоку. Затем текущий процесс или поток получает владение блокировкой (если ещё не владеет ею), и уровень рекурсии внутри блокировки увеличивается на единицу, возвращая значение `True`. Обратите внимание, что имеется несколько различий в поведении этого первого аргумента по сравнению с реализацией [`threading.RLock.acquire()`](https://python-all.ru/2.7/library/threading.html#threading.RLock.acquire), начиная с названия самого аргумента.

При вызове с аргументом *block*, установленным в `False`, не блокируется. Если блокировка уже была захвачена (и, следовательно, принадлежит) другим процессом или потоком, текущий процесс или поток не получает владение, а уровень рекурсии внутри блокировки не изменяется, возвращая значение `False`. Если блокировка находится в разблокированном состоянии, текущий процесс или поток получает владение, а уровень рекурсии увеличивается, возвращая значение `True`.

Использование и поведение аргумента *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).

#### `release()`

Освобождает блокировку, уменьшая уровень рекурсии. Если после уменьшения уровень рекурсии становится нулевым, переводит блокировку в разблокированное состояние (не принадлежит ни одному процессу или потоку), и если какие-либо другие процессы или потоки ожидают разблокировки, позволяет ровно одному из них продолжить. Если после уменьшения уровень рекурсии всё ещё ненулевой, блокировка остаётся заблокированной и принадлежащей вызывающему процессу или потоку.

Вызывайте этот метод только когда вызывающий процесс или поток владеет блокировкой. Исключение [`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).

#### `class multiprocessing.Semaphore([value])`

Объект семафора: близкий аналог [`threading.Semaphore`](https://python-all.ru/2.7/library/threading.html#threading.Semaphore).

Единственное отличие от его близкого аналога: первый аргумент его метода `acquire` называется *block*, и он поддерживает необязательный второй аргумент *timeout*, в соответствии с [`Lock.acquire()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Lock.acquire).

> **Примечание**
>
> 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.
>
> На Mac OS X `sem_timedwait` не поддерживается, поэтому вызов `acquire()` с тайм-аутом будет эмулировать поведение этой функции с помощью спящего цикла.

> **Примечание**
>
> Если сигнал 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).
>
> Это отличается от поведения [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading), где SIGINT игнорируется, пока выполняются эквивалентные блокирующие вызовы.

> **Примечание**
>
> Некоторые функции этого пакета требуют работающей реализации общего семафора в операционной системе. При её отсутствии модуль `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) для получения дополнительной информации.

### 16.6.2.6. Разделяемые [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes) объекты

Можно создавать общие объекты с помощью разделяемой памяти, которые могут наследоваться дочерними процессами.

#### `multiprocessing.Value(typecode_or_type, *args[, lock])`

Возвращает объект [`ctypes`](https://python-all.ru/2.7/library/ctypes.html#module-ctypes), выделенный из общей памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для этого объекта.

*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). *\*args* передаётся конструктору типа.

Если *блокировка* равна `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`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".

Операции, подобные `+=`, которые включают чтение и запись, не являются атомарными. Поэтому, если, например, требуется атомарно увеличить разделяемое значение, недостаточно просто сделать

```python
counter.value += 1
```

При условии, что связанная блокировка рекурсивна (по умолчанию она такой и является), можно вместо этого сделать

```python
with counter.get_lock():
    counter.value += 1
```

Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.

#### `multiprocessing.Array(typecode_or_type, size_or_initializer, *, lock=True)`

Возвращает массив ctypes, выделенный из разделяемой памяти. По умолчанию возвращаемое значение на самом деле является синхронизированной обёрткой для массива.

*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо ctypes-тип, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.

Если *блокировка* равен `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`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».

Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.

Обратите внимание, что массив типа [`ctypes.c_char`](https://python-all.ru/2.7/library/ctypes.html#ctypes.c_char) имеет атрибуты *value* и *raw*, которые позволяют использовать его для хранения и извлечения строк.

#### 16.6.2.6.1. Модуль [`multiprocessing.sharedctypes`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.sharedctypes)

Модуль [`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) из разделяемой памяти, которые могут быть унаследованы дочерними процессами.

> **Примечание**
>
> Хотя можно сохранить указатель в разделяемой памяти, следует помнить, что он будет ссылаться на местоположение в адресном пространстве конкретного процесса. Однако указатель, скорее всего, будет недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может привести к аварийному завершению.

#### `multiprocessing.sharedctypes.RawArray(typecode_or_type, size_or_initializer)`

Возвращает массив ctypes, выделенный из разделяемой памяти.

*typecode\_or\_type* определяет тип элементов возвращаемого массива: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). Если *size\_or\_initializer* – целое число, то оно определяет длину массива, и массив будет изначально заполнен нулями. В противном случае *size\_or\_initializer* – это последовательность, которая используется для инициализации массива, и её длина определяет длину массива.

Обратите внимание, что установка и получение элемента потенциально неатомарны – используйте вместо этого [`Array()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.Array), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.

#### `multiprocessing.sharedctypes.RawValue(typecode_or_type, *args)`

Возвращает объект ctypes, выделенный из разделяемой памяти.

*typecode\_or\_type* определяет тип возвращаемого объекта: это либо тип ctypes, либо однобуквенный код типа, используемый модулем [`array`](https://python-all.ru/2.7/library/array.html#module-array). *\*args* передаётся конструктору типа.

Обратите внимание, что установка и получение значения потенциально неатомарны – используйте вместо этого [`Value()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.Value), чтобы гарантировать автоматическую синхронизацию доступа с помощью блокировки.

Обратите внимание, что массив [`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).

#### `multiprocessing.sharedctypes.Array(typecode_or_type, size_or_initializer, *args[, lock])`

То же, что и [`RawArray()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.RawArray), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного массива ctypes.

Если *блокировка* равна `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`, то доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет "процессобезопасным".

Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.

#### `multiprocessing.sharedctypes.Value(typecode_or_type, *args[, lock])`

То же, что и [`RawValue()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.sharedctypes.RawValue), за исключением того, что в зависимости от значения *блокировка* может возвращаться процессобезопасная синхронизирующая обёртка вместо необработанного объекта ctypes.

Если *блокировка* равен `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`, доступ к возвращаемому объекту не будет автоматически защищён блокировкой, поэтому он не обязательно будет «процесс-безопасным».

Обратите внимание, что *блокировка* – это аргумент, передаваемый только по ключевому слову.

#### `multiprocessing.sharedctypes.copy(obj)`

Возвращает объект ctypes, выделенный в общей памяти и являющийся копией объекта ctypes *obj*.

#### `multiprocessing.sharedctypes.synchronized(obj[, lock])`

Возвращает процесс-безопасную обёртку для объекта ctypes, которая использует *блокировка* для синхронизации доступа. Если *блокировка* равен `None` (по умолчанию), объект [`multiprocessing.RLock`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.RLock) создаётся автоматически.

Синхронизированная обёртка будет иметь два дополнительных метода (помимо методов обёрнутого объекта): `get_obj()` возвращает обёрнутый объект, а `get_lock()` – объект блокировки, используемый для синхронизации.

Обратите внимание, что доступ к объекту ctypes через обёртку может быть значительно медленнее, чем доступ к исходному объекту ctypes.

В таблице ниже сравнивается синтаксис создания разделяемых объектов ctypes из общей памяти с обычным синтаксисом ctypes. (В таблице `MyStruct` – это некоторый подкласс [`ctypes.Structure`](https://python-all.ru/2.7/library/ctypes.html#ctypes.Structure).)

| ctypes | sharedctypes с типом | sharedctypes с typecode |
| --- | --- | --- |
| c\_double(2.4) | RawValue(c\_double, 2.4) | RawValue(‘d’, 2.4) |
| MyStruct(4, 6) | RawValue(MyStruct, 4, 6) |  |
| (c\_short \* 7)() | RawArray(c\_short, 7) | RawArray(‘h’, 7) |
| (c\_int \* 3)(9, 2, 8) | RawArray(c\_int, (9, 2, 8)) | RawArray(‘i’, (9, 2, 8)) |

Ниже приведён пример, в котором дочерний процесс изменяет несколько объектов ctypes:

```python
from multiprocessing import Process, Lock
from multiprocessing.sharedctypes import Value, Array
from ctypes import Structure, c_double

class Point(Structure):
    _fields_ = [('x', c_double), ('y', c_double)]

def modify(n, x, s, A):
    n.value **= 2
    x.value **= 2
    s.value = s.value.upper()
    for a in A:
        a.x **= 2
        a.y **= 2

if __name__ == '__main__':
    lock = Lock()

    n = Value('i', 7)
    x = Value(c_double, 1.0/3.0, lock=False)
    s = Array('c', 'hello world', lock=lock)
    A = Array(Point, [(1.875,-6.25), (-5.75,2.0), (2.375,9.5)], lock=lock)

    p = Process(target=modify, args=(n, x, s, A))
    p.start()
    p.join()

    print n.value
    print x.value
    print s.value
    print [(a.x, a.y) for a in A]
```

Выводятся следующие результаты:

```text
49
0.1111111111111111
HELLO WORLD
[(3.515625, 39.0625), (33.0625, 4.0), (5.640625, 90.25)]
```

### 16.6.2.7. Менеджеры

Менеджеры позволяют создавать данные, которыми могут обмениваться разные процессы. Объект менеджера управляет серверным процессом, который ведает *разделяемыми объектами*. Другие процессы могут получать доступ к разделяемым объектам через прокси.

#### `multiprocessing.Manager()`

Возвращает запущенный объект [`SyncManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.SyncManager), который можно использовать для совместного использования объектов между процессами. Возвращаемый объект менеджера соответствует порождённому дочернему процессу и имеет методы, которые создают разделяемые объекты и возвращают соответствующие прокси.

Процессы менеджера завершаются, как только они будут собраны сборщиком мусора или завершится их родительский процесс. Классы менеджера определены в модуле [`multiprocessing.managers`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.managers):

#### `class multiprocessing.managers.BaseManager([address[, authkey]])`

Создает объект BaseManager.

После создания необходимо вызвать [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) или `get_server().serve_forever()`, чтобы объект менеджера ссылался на запущенный процесс менеджера.

*address* – адрес, на котором процесс менеджера ожидает новые подключения. Если *address* равно `None`, то выбирается произвольный адрес.

*authkey* – это ключ аутентификации, который будет использоваться для проверки подлинности входящих соединений с серверным процессом. Если *authkey* равен `None`, то `current_process().authkey`. В противном случае используется *authkey*, и он должен быть строкой.

#### `start([initializer[, initargs]])`

Запускает подпроцесс для запуска менеджера. Если *initializer* не равно `None`, то подпроцесс вызовет `initializer(*initargs)` при запуске.

#### `get_server()`

Возвращает объект `Server`, представляющий реальный сервер, управляемый менеджером. Объект `Server` поддерживает метод `serve_forever()`:

```python
>>> from multiprocessing.managers import BaseManager
>>> manager = BaseManager(address=('', 50000), authkey='abc')
>>> server = manager.get_server()
>>> server.serve_forever()
```

`Server` также имеет атрибут [`address`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.address).

#### `connect()`

Подключает локальный объект менеджера к удаленному процессу менеджера:

```python
>>> from multiprocessing.managers import BaseManager
>>> m = BaseManager(address=('127.0.0.1', 5000), authkey='abc')
>>> m.connect()
```

#### `shutdown()`

Останавливает процесс, используемый менеджером. Это доступно только если [`start()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.start) был использован для запуска серверного процесса.

Этот метод можно вызывать несколько раз.

#### `register(typeid[, callable[, proxytype[, exposed[, method_to_typeid[, create_method]]]]])`

Метод класса, который можно использовать для регистрации типа или вызываемого объекта в классе менеджера.

*typeid* – это «идентификатор типа», который используется для идентификации определенного типа разделяемого объекта. Должен быть строкой.

*callable* – это вызываемый объект, используемый для создания объектов данного идентификатора типа. Если экземпляр менеджера будет создан с помощью метода класса `from_address()` или если аргумент *create\_method* равен `False`, то его можно оставить как `None`.

*proxytype* – это подкласс [`BaseProxy`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy), который используется для создания прокси для разделяемых объектов с данным *typeid*. Если `None`, то класс прокси создается автоматически.

*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__) и имя которого не начинается с `'_'`.)

*method\_to\_typeid* – это отображение, используемое для указания возвращаемого типа тех открытых методов, которые должны возвращать прокси. Оно отображает имена методов в строки typeid. (Если *method\_to\_typeid* равно `None`, то вместо него используется `proxytype._method_to_typeid_`, если он существует.) Если имя метода не является ключом этого отображения или отображение равно `None`, то объект, возвращаемый методом, будет скопирован по значению.

*create\_method* определяет, следует ли создать метод с именем *typeid*, который можно использовать, чтобы указать серверному процессу создать новый разделяемый объект и вернуть для него прокси. По умолчанию `True`.

Экземпляры [`BaseManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager) также имеют одно свойство только для чтения:

#### `address`

Адрес, используемый менеджером.

#### `class multiprocessing.managers.SyncManager`

Подкласс [`BaseManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager), который можно использовать для синхронизации процессов. Объекты этого типа возвращаются `multiprocessing.Manager()`.

Он также поддерживает создание разделяемых списков и словарей.

#### `BoundedSemaphore([value])`

Создать общий объект [`threading.BoundedSemaphore`](https://python-all.ru/2.7/library/threading.html#threading.BoundedSemaphore) и вернуть для него прокси.

#### `Condition([lock])`

Создать общий объект [`threading.Condition`](https://python-all.ru/2.7/library/threading.html#threading.Condition) и вернуть прокси для него.

Если *блокировка* предоставлена, то она должна быть прокси для объекта [`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).

#### `Event()`

Создать общий объект [`threading.Event`](https://python-all.ru/2.7/library/threading.html#threading.Event) и вернуть прокси для него.

#### `Lock()`

Создать общий объект [`threading.Lock`](https://python-all.ru/2.7/library/threading.html#threading.Lock) и вернуть прокси для него.

#### `Namespace()`

Создать общий объект [`Namespace`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.Namespace) и вернуть прокси для него.

#### `Queue([maxsize])`

Создать общий объект [`Queue.Queue`](https://python-all.ru/2.7/library/queue.html#Queue.Queue) и вернуть прокси для него.

#### `RLock()`

Создать общий объект [`threading.RLock`](https://python-all.ru/2.7/library/threading.html#threading.RLock) и вернуть прокси для него.

#### `Semaphore([value])`

Создать общий объект [`threading.Semaphore`](https://python-all.ru/2.7/library/threading.html#threading.Semaphore) и вернуть прокси для него.

#### `Array(typecode, sequence)`

Создать массив и вернуть для него прокси.

#### `Value(typecode, value)`

Создать объект с атрибутом `value`, доступным для записи, и вернуть прокси для него.

#### `dict()`

#### `dict(mapping)`

#### `dict(sequence)`

Создать общий объект `dict` и вернуть прокси для него.

#### `list()`

#### `list(sequence)`

Создать общий объект `list` и вернуть прокси для него.

> **Примечание**
>
> Изменения изменяемых значений или элементов в прокси-объектах словарей и списков не будут распространяться через менеджер, поскольку прокси не может отследить, когда его значения или элементы изменяются. Чтобы изменить такой элемент, можно переназначить изменённый объект обратно в прокси-контейнер:
>
> ```python
> # создать прокси для списка и добавить изменяемый объект (словарь)
> lproxy = manager.list()
> lproxy.append({})
> # теперь изменить словарь
> d = lproxy[0]
> d['a'] = 1
> d['b'] = 2
> # на данный момент изменения в d еще не синхронизированы, но при
> # при переприсваивании словаря прокси получает уведомление об изменении
> lproxy[0] = d
> ```

#### `class multiprocessing.managers.Namespace`

Тип, который можно зарегистрировать с помощью [`SyncManager`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.SyncManager).

Объект пространства имён не имеет открытых методов, но имеет изменяемые атрибуты. Его представление показывает значения его атрибутов.

Однако при использовании прокси для объекта пространства имён атрибут, начинающийся с `'_'`, будет атрибутом прокси, а не атрибутом референта:

```pycon
>>> manager = multiprocessing.Manager()
>>> Global = manager.Namespace()
>>> Global.x = 10
>>> Global.y = 'hello'
>>> Global._z = 12.3    # это атрибут прокси
>>> print Global
Namespace(x=10, y='hello')
```

#### 16.6.2.7.1. Настраиваемые менеджеры

Чтобы создать собственный менеджер, создаётся подкласс [`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) для регистрации новых типов или вызываемых объектов в классе менеджера. Например:

```python
from multiprocessing.managers import BaseManager

class MathsClass(object):
    def add(self, x, y):
        return x + y
    def mul(self, x, y):
        return x * y

class MyManager(BaseManager):
    pass

MyManager.register('Maths', MathsClass)

if __name__ == '__main__':
    manager = MyManager()
    manager.start()
    maths = manager.Maths()
    print maths.add(4, 3)         # выводит 7
    print maths.mul(7, 8)         # выводит 56
```

#### 16.6.2.7.2. Использование удалённого менеджера

Можно запустить сервер менеджера на одной машине и позволить клиентам использовать его с других машин (при условии, что соответствующие брандмауэры это разрешают).

Выполнение следующих команд создаёт сервер для одной общей очереди, к которой удалённые клиенты могут обращаться:

```python
>>> from multiprocessing.managers import BaseManager
>>> import Queue
>>> queue = Queue.Queue()
>>> class QueueManager(BaseManager): pass
>>> QueueManager.register('get_queue', callable=lambda:queue)
>>> m = QueueManager(address=('', 50000), authkey='abracadabra')
>>> s = m.get_server()
>>> s.serve_forever()
```

Один клиент может получить доступ к серверу следующим образом:

```python
>>> from multiprocessing.managers import BaseManager
>>> class QueueManager(BaseManager): pass
>>> QueueManager.register('get_queue')
>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')
>>> m.connect()
>>> queue = m.get_queue()
>>> queue.put('hello')
```

Другой клиент также может использовать его:

```python
>>> from multiprocessing.managers import BaseManager
>>> class QueueManager(BaseManager): pass
>>> QueueManager.register('get_queue')
>>> m = QueueManager(address=('foo.bar.org', 50000), authkey='abracadabra')
>>> m.connect()
>>> queue = m.get_queue()
>>> queue.get()
'hello'
```

Локальные процессы также могут получить доступ к этой очереди, используя приведённый выше код на клиенте для удалённого доступа к ней:

```python
>>> from multiprocessing import Process, Queue
>>> from multiprocessing.managers import BaseManager
>>> class Worker(Process):
...     def __init__(self, q):
...         self.q = q
...         super(Worker, self).__init__()
...     def run(self):
...         self.q.put('local hello')
...
>>> queue = Queue()
>>> w = Worker(queue)
>>> w.start()
>>> class QueueManager(BaseManager): pass
...
>>> QueueManager.register('get_queue', callable=lambda: queue)
>>> m = QueueManager(address=('', 50000), authkey='abracadabra')
>>> s = m.get_server()
>>> s.serve_forever()
```

### 16.6.2.8. Прокси-объекты

Прокси – это объект, который *ссылается* на общий объект, находящийся (предположительно) в другом процессе. Общий объект называется *референтом* этого прокси. Несколько объектов-прокси могут иметь один и тот же референт.

Прокси-объект имеет методы, которые вызывают соответствующие методы его референта (хотя не каждый метод референта обязательно будет доступен через прокси). Прокси обычно можно использовать почти так же, как и его референт:

```pycon
>>> from multiprocessing import Manager
>>> manager = Manager()
>>> l = manager.list([i*i for i in range(10)])
>>> print l
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> print repr(l)
<ListProxy object, typeid 'list' at 0x...>
>>> l[4]
16
>>> l[2:5]
[4, 9, 16]
```

Обратите внимание, что применение [`str()`](https://python-all.ru/2.7/library/functions.html#str) к прокси вернёт представление референта, тогда как применение [`repr()`](https://python-all.ru/2.7/library/functions.html#repr) вернёт представление прокси.

Важная особенность прокси-объектов в том, что они могут быть сериализованы (picklable) и поэтому могут передаваться между процессами. Однако обратите внимание: если прокси отправляется в процесс соответствующего менеджера, то при десериализации будет получен сам референт. Это означает, например, что один общий объект может содержать другой:

```pycon
>>> a = manager.list()
>>> b = manager.list()
>>> a.append(b)         # референт a теперь содержит референт b
>>> print a, b
[[]] []
>>> b.append('hello')
>>> print a, b
[['hello']] ['hello']
```

> **Примечание**
>
> Типы прокси в [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) не поддерживают сравнение по значению. Так, например:
>
> ```pycon
> >>> manager.list([1,2,3]) == [1,2,3]
> False
> ```
>
> При сравнении следует просто использовать копию референта.

#### `class multiprocessing.managers.BaseProxy`

Прокси-объекты являются экземплярами подклассов [`BaseProxy`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy).

#### `_callmethod(methodname[, args[, kwds]])`

Вызывает метод референта прокси и возвращает результат.

Если `proxy` – это прокси, чей референт – `obj`, то выражение

```python
proxy._callmethod(methodname, args, kwds)
```

вычислит выражение

```python
getattr(obj, methodname)(*args, **kwds)
```

в процессе менеджера.

Возвращаемое значение будет копией результата вызова или прокси для нового разделяемого объекта – см. документацию по аргументу *method\_to\_typeid* функции [`BaseManager.register()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseManager.register).

Если при вызове возникло исключение, то оно повторно возбуждается с помощью [`_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).

Обратите внимание, что исключение будет возбуждаться, если *methodname* не был *экспонирован*.

Пример использования [`_callmethod()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.managers.BaseProxy._callmethod):

```pycon
>>> l = manager.list(range(10))
>>> l._callmethod('__len__')
10
>>> l._callmethod('__getslice__', (2, 7))   # эквивалентно `l[2:7]`
[2, 3, 4, 5, 6]
>>> l._callmethod('__getitem__', (20,))     # эквивалентно `l[20]`
Traceback (most recent call last):
...
IndexError: list index out of range
```

#### `_getvalue()`

Возвращает копию референта.

Если референт не может быть сериализован (unpicklable), то это вызовет исключение.

#### `__repr__()`

Возвращает представление прокси-объекта.

#### `__str__()`

Возвращает представление референта.

#### 16.6.2.8.1. Очистка

Прокси-объект использует колбэк слабой ссылки: когда он собирается сборщиком мусора, он отменяет свою регистрацию у менеджера, которому принадлежит его референт.

Разделяемый объект удаляется из процесса менеджера, когда на него больше не ссылается ни один прокси.

### 16.6.2.9. Пулы процессов

Можно создать пул процессов, который будет выполнять задачи, отправленные ему с помощью класса `Pool`.

#### `class multiprocessing.Pool([processes[, initializer[, initargs[, maxtasksperchild]]]])`

Объект пула процессов, который управляет пулом рабочих процессов, которым можно отправлять задачи. Он поддерживает асинхронные результаты с тайм-аутами и колбэками, а также имеет параллельную реализацию map.

*processes* определяет количество используемых рабочих процессов. Если *processes* равно `None`, то используется число, возвращаемое `cpu_count()`. Если *initializer* не равно `None`, то каждый рабочий процесс вызовет `initializer(*initargs)` при запуске.

Обратите внимание, что методы объекта пула должны вызываться только тем процессом, который создал пул.

Новое в версии 2.7: *maxtasksperchild* – количество задач, которое рабочий процесс может выполнить прежде чем завершится и будет заменён новым рабочим процессом, чтобы освободить неиспользуемые ресурсы. Значение по умолчанию *maxtasksperchild* равно `None`, что означает, что рабочие процессы будут жить столько же, сколько и пул.

> **Примечание**
>
> Рабочие процессы внутри `Pool` обычно существуют в течение всего времени работы очереди пула. Часто используемый в других системах (например, Apache, mod\_wsgi и т.д.) подход для освобождения ресурсов, удерживаемых рабочими процессами, заключается в том, чтобы позволить рабочему процессу в пуле выполнить только определённый объём работы, после чего он завершается, очищается, и на его место запускается новый процесс. Аргумент *maxtasksperchild* функции `Pool` предоставляет эту возможность конечному пользователю.

#### `apply(func[, args[, kwds]])`

Эквивалент встроенной функции [`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* выполняется только в одном из рабочих процессов пула.

#### `apply_async(func[, args[, kwds[, callback]]])`

Вариант метода [`apply()`](https://python-all.ru/2.7/library/functions.html#apply), возвращающий объект результата.

Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.

#### `map(func, iterable[, chunksize])`

Параллельный аналог встроенной функции [`map()`](https://python-all.ru/2.7/library/functions.html#map) (однако поддерживает только один аргумент *iterable*). Блокирует выполнение до готовности результата.

Этот метод разбивает итерируемый объект на несколько частей (chunks), которые отправляет в пул процессов как отдельные задачи. Приблизительный размер этих частей можно задать, установив *chunksize* в положительное целое число.

#### `map_async(func, iterable[, chunksize[, callback]])`

Вариант метода [`map()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map), возвращающий объект результата.

Если указан *колбэк*, это должен быть вызываемый объект, принимающий один аргумент. Когда результат готов, к нему применяется *колбэк* (если вызов не завершился ошибкой). *Колбэк* должен выполняться немедленно, иначе поток, обрабатывающий результаты, будет заблокирован.

#### `imap(func, iterable[, chunksize])`

An equivalent of [`itertools.imap()`](https://python-all.ru/2.7/library/itertools.html#itertools.imap).

Аргумент *chunksize* совпадает с тем, что используется в методе [`map()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.map). Для очень длинных итерируемых объектов использование большого значения *chunksize* может привести к завершению задачи **значительно** быстрее, чем при использовании значения по умолчанию `1`.

Кроме того, если *chunksize* равно `1`, то метод `next()` итератора, возвращаемого методом [`imap()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), имеет необязательный параметр *timeout*: `next(timeout)` возбудит `multiprocessing.TimeoutError`, если результат не может быть возвращён в течение *timeout* секунд.

#### `imap_unordered(func, iterable[, chunksize])`

То же, что и [`imap()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.imap), за исключением того, что порядок результатов из возвращаемого итератора считается произвольным. (Только когда есть только один рабочий процесс, порядок гарантированно будет «правильным».)

#### `close()`

Предотвращает отправку новых задач в пул. После завершения всех задач рабочие процессы завершаются.

#### `terminate()`

Немедленно останавливает рабочие процессы, не дожидаясь завершения незаконченных задач. Когда объект пула будет собран сборщиком мусора, [`terminate()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.multiprocessing.Pool.terminate) будет вызван немедленно.

#### `join()`

Ожидает завершения рабочих процессов. Перед использованием [`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).

#### `class multiprocessing.pool.AsyncResult`

Класс результата, возвращаемого `Pool.apply_async()` и `Pool.map_async()`.

#### `get([timeout])`

Возвращает результат, когда он будет получен. Если *timeout* не равен `None` и результат не поступает в течение *timeout* секунд, то вызывается `multiprocessing.TimeoutError`. Если удалённый вызов вызвал исключение, то это исключение будет повторно возбуждено [`get()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.pool.AsyncResult.get).

#### `wait([timeout])`

Ожидает, пока результат не станет доступен или пока не пройдёт *timeout* секунд.

#### `ready()`

Возвращает, завершён ли вызов.

#### `successful()`

Возвращает, завершился ли вызов без возникновения исключения. Возбуждает [`AssertionError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.AssertionError), если результат ещё не готов.

Следующий пример демонстрирует использование пула:

```python
from multiprocessing import Pool
import time

def f(x):
    return x*x

if __name__ == '__main__':
    pool = Pool(processes=4)              # запустить 4 рабочих процесса

    result = pool.apply_async(f, (10,))   # вычислить "f(10)" асинхронно в одном процессе
    print result.get(timeout=1)           # выводит "100", если компьютер не *очень* медленный

    print pool.map(f, range(10))          # выводит "[0, 1, 4,..., 81]"

    it = pool.imap(f, range(10))
    print it.next()                       # выводит "0"
    print it.next()                       # выводит "1"
    print it.next(timeout=1)              # выводит "4", если компьютер не *очень* медленный

    result = pool.apply_async(time.sleep, (10,))
    print result.get(timeout=1)           # вызывает multiprocessing.TimeoutError
```

### 16.6.2.10. Слушатели и клиенты

Обычно передача сообщений между процессами осуществляется с помощью очередей или с помощью объектов [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection), возвращаемых [`Pipe()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Pipe).

Однако модуль [`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).

#### `multiprocessing.connection.deliver_challenge(connection, authkey)`

Отправляет случайно сгенерированное сообщение на другой конец соединения и ожидает ответа.

Если ответ совпадает с дайджестом сообщения, используя *authkey* в качестве ключа, то на другой конец соединения отправляется приветственное сообщение. В противном случае возбуждается [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError).

#### `multiprocessing.connection.answer_challenge(connection, authkey)`

Принимает сообщение, вычисляет дайджест сообщения, используя *authkey* в качестве ключа, а затем отправляет дайджест обратно.

Если приветственное сообщение не получено, то [`AuthenticationError`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.AuthenticationError) возбуждается.

#### `multiprocessing.connection.Client(address[, family[, authenticate[, authkey]]])`

Пытается установить соединение с слушателем, использующим адрес *address*, возвращая [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection).

Тип соединения определяется аргументом *family*, но его обычно можно опустить, поскольку он обычно определяется по формату *address*. (См. [Address Formats](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing-address-formats))

Если *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).

#### `class multiprocessing.connection.Listener([address[, family[, backlog[, authenticate[, authkey]]]]])`

Обёртка для привязанного сокета или именованного канала Windows, который «прослушивает» подключения.

*address* – это адрес, который будет использоваться привязанным сокетом или именованным каналом объекта слушателя.

> **Примечание**
>
> Если используется адрес ‘0.0.0.0’, он не будет доступной точкой подключения в Windows. Если требуется доступная точка подключения, следует использовать ‘127.0.0.1’.

*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).

Если объект listener использует сокет, то *backlog* (по умолчанию 1) передаётся методу [`listen()`](https://python-all.ru/2.7/library/socket.html#socket.socket.listen) сокета после его привязки.

Если *authenticate* равен `True` (по умолчанию `False`) или *authkey* не равен `None`, то используется дайджест-аутентификация.

Если *authkey* является строкой, то она будет использоваться в качестве ключа аутентификации; в противном случае она должна быть `None`.

Если *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).

#### `accept()`

Принимает соединение на привязанном сокете или именованном канале объекта listener и возвращает объект [`Connection`](https://python-all.ru/2.7/library/multiprocessing.html#Connection). Если предпринята попытка аутентификации и она не удалась, возбуждается `AuthenticationError`.

#### `close()`

Закрывает привязанный сокет или именованный канал объекта listener. Этот метод вызывается автоматически при сборке мусора listener'а. Однако рекомендуется вызывать его явно.

Объекты Listener имеют следующие свойства только для чтения:

#### `address`

Адрес, используемый объектом Listener.

#### `last_accepted`

Адрес, с которого поступило последнее принятое соединение. Если он недоступен, то равен `None`.

Модуль определяет следующие исключения:

#### `exception multiprocessing.connection.ProcessError`

Базовый класс всех исключений [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing).

#### `exception multiprocessing.connection.BufferTooShort`

Исключение, возбуждаемое [`Connection.recv_bytes_into()`](https://python-all.ru/2.7/library/multiprocessing.html#Connection.recv_bytes_into), когда предоставленный буфер слишком мал для чтения сообщения.

#### `exception multiprocessing.connection.AuthenticationError`

Возбуждается при ошибке аутентификации.

#### `exception multiprocessing.connection.TimeoutError`

Возбуждается методами с тайм-аутом по истечении тайм-аута.

**Примеры**

Следующий серверный код создаёт listener, который использует `'secret password'` в качестве ключа аутентификации. Затем он ожидает соединения и отправляет некоторые данные клиенту:

```python
from multiprocessing.connection import Listener
from array import array

address = ('localhost', 6000)     # семейство определяется как 'AF_INET'
listener = Listener(address, authkey='secret password')

conn = listener.accept()
print 'connection accepted from', listener.last_accepted

conn.send([2.25, None, 'junk', float])

conn.send_bytes('hello')

conn.send_bytes(array('i', [42, 1729]))

conn.close()
listener.close()
```

Следующий код подключается к серверу и получает от него некоторые данные:

```python
from multiprocessing.connection import Client
from array import array

address = ('localhost', 6000)
conn = Client(address, authkey='secret password')

print conn.recv()                 # => [2.25, None, 'junk', float]

print conn.recv_bytes()            # => 'hello'

arr = array('i', [0, 0, 0, 0, 0])
print conn.recv_bytes_into(arr)     # => 8
print arr                         # => array('i', [42, 1729, 0, 0, 0])

conn.close()
```

#### 16.6.2.10.1. Форматы адресов

- Адрес `'AF_INET'` – это кортеж вида `(hostname, port)`, где *hostname* – строка, а *port* – целое число.
- Адрес `'AF_UNIX'` – это строка, представляющая имя файла в файловой системе.
- **Адрес `'AF_PIPE'` – это строка вида**

  `r'\.\pipe{PipeName}'`. Чтобы использовать [`Client()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.connection.Client) для подключения к именованному каналу на удалённом компьютере *ServerName*, следует использовать адрес вида `r'\ServerName\pipe{PipeName}'`.

Обратите внимание: любая строка, начинающаяся с двух обратных слешей, по умолчанию считается адресом `'AF_PIPE'`, а не `'AF_UNIX'`.

### 16.6.2.11. Ключи аутентификации

При использовании [`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) для обеспечения дайджест-аутентификации.

Ключ аутентификации – это строка, которую можно рассматривать как пароль: как только соединение установлено, обе стороны требуют подтверждения, что другая сторона знает ключ аутентификации. (Доказательство того, что обе стороны используют один и тот же ключ, **не** подразумевает отправку ключа по соединению.)

Если запрошена аутентификация, но ключ аутентификации не указан, то используется возвращаемое значение `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), который создаёт текущий процесс. Это означает, что (по умолчанию) все процессы многопроцессной программы используют один ключ аутентификации, который можно применять для настройки соединений между ними.

Подходящие ключи аутентификации также можно сгенерировать с помощью [`os.urandom()`](https://python-all.ru/2.7/library/os.html#os.urandom).

### 16.6.2.12. Логирование

Доступна поддержка журналирования. Однако обратите внимание, что пакет [`logging`](https://python-all.ru/2.7/library/logging.html#module-logging) не использует блокировки, разделяемые между процессами, поэтому сообщения от разных процессов могут перемешиваться (в зависимости от типа обработчика).

#### `multiprocessing.get_logger()`

Возвращает логгер, используемый модулем [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing). При необходимости создаётся новый.

При первом создании логгер имеет уровень `logging.NOTSET` и не имеет обработчика по умолчанию. Сообщения, отправленные этому логгеру, по умолчанию не распространяются на корневой логгер.

Обратите внимание, что в Windows дочерние процессы наследуют только уровень логгера родительского процесса – любые другие настройки логгера не наследуются.

#### `multiprocessing.log_to_stderr()`

Эта функция вызывает [`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'`.

Ниже приведён пример сеанса с включённым журналированием:

```python
>>> import multiprocessing, logging
>>> logger = multiprocessing.log_to_stderr()
>>> logger.setLevel(logging.INFO)
>>> logger.warning('doomed')
[WARNING/MainProcess] doomed
>>> m = multiprocessing.Manager()
[INFO/SyncManager-...] child process calling self.run()
[INFO/SyncManager-...] created temp directory /.../pymp-...
[INFO/SyncManager-...] manager serving at '/.../listener-...'
>>> del m
[INFO/MainProcess] sending shutdown message to manager
[INFO/SyncManager-...] manager exiting with exitcode 0
```

В дополнение к этим двум функциям логирования, модуль multiprocessing также предоставляет два дополнительных атрибута уровня логирования: `SUBWARNING` и `SUBDEBUG`. В таблице ниже показано, где они находятся в обычной иерархии уровней.

| Уровень | Числовое значение |
| --- | --- |
| `SUBWARNING` | 25 |
| `SUBDEBUG` | 5 |

Полную таблицу уровней журналирования см. в модуле [`logging`](https://python-all.ru/2.7/library/logging.html#module-logging).

Эти дополнительные уровни логирования используются в основном для определённых отладочных сообщений в модуле multiprocessing. Ниже приведён тот же пример, что и выше, но с включённым `SUBDEBUG`:

```python
>>> import multiprocessing, logging
>>> logger = multiprocessing.log_to_stderr()
>>> logger.setLevel(multiprocessing.SUBDEBUG)
>>> logger.warning('doomed')
[WARNING/MainProcess] doomed
>>> m = multiprocessing.Manager()
[INFO/SyncManager-...] child process calling self.run()
[INFO/SyncManager-...] created temp directory /.../pymp-...
[INFO/SyncManager-...] manager serving at '/.../pymp-djGBXN/listener-...'
>>> del m
[SUBDEBUG/MainProcess] finalizer calling ...
[INFO/MainProcess] sending shutdown message to manager
[DEBUG/SyncManager-...] manager received shutdown message
[SUBDEBUG/SyncManager-...] calling <Finalize object, callback=unlink, ...
[SUBDEBUG/SyncManager-...] finalizer calling <built-in function unlink> ...
[SUBDEBUG/SyncManager-...] calling <Finalize object, dead>
[SUBDEBUG/SyncManager-...] finalizer calling <function rmtree at 0x5aa730> ...
[INFO/SyncManager-...] manager exiting with exitcode 0
```

### 16.6.2.13. Модуль [`multiprocessing.dummy`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing.dummy)

[`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).

## 16.6.3. Рекомендации по программированию

Существуют определённые рекомендации и идиомы, которых следует придерживаться при использовании [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing).

### 16.6.3.1. Все платформы

Избегайте разделяемого состояния

> По возможности следует стараться избегать передачи больших объёмов данных между процессами.
>
> Вероятно, лучше придерживаться использования очередей или каналов для связи между процессами, а не использовать низкоуровневые примитивы синхронизации из модуля [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading).

Сериализуемость

> Убедитесь, что аргументы методов прокси-объектов сериализуемы.

Потокобезопасность прокси

> Не используйте прокси-объект из более чем одного потока, если не защищаете его блокировкой.
>
> (С разными процессами, использующими *один и тот же* прокси, проблем не возникает.)

Присоединение процессов-зомби

> В 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) для завершённого процесса присоединяет его. Тем не менее, рекомендуется явно присоединять все запущенные процессы.

Лучше наследовать, чем упаковывать/распаковывать

> В Windows многие типы из [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) должны быть сериализуемыми (picklable), чтобы дочерние процессы могли их использовать. Однако обычно следует избегать отправки общих объектов другим процессам через каналы или очереди. Вместо этого следует организовать программу так, чтобы процесс, которому требуется доступ к общему ресурсу, созданному в другом месте, мог унаследовать его от родительского процесса.

Избегайте завершения процессов

> Использование метода [`Process.terminate`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) для остановки процесса может привести к тому, что любые разделяемые ресурсы (такие как блокировки, семафоры, каналы и очереди), которые в данный момент используются процессом, станут повреждёнными или недоступными для других процессов.
>
> Поэтому, вероятно, лучше рассматривать использование [`Process.terminate`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.terminate) только для процессов, которые никогда не используют разделяемые ресурсы.

Присоединение процессов, использующих очереди

> Имейте в виду, что процесс, поместивший элементы в очередь, будет ждать перед завершением, пока все буферизированные элементы не будут переданы потоком «feeder» в базовый канал. (Дочерний процесс может вызвать метод [`cancel_join_thread()`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Queue.cancel_join_thread) очереди, чтобы избежать такого поведения.)
>
> Это означает, что при использовании очереди нужно убедиться, что все элементы, помещённые в очередь, будут в конечном итоге удалены до того, как процесс будет присоединён. В противном случае нельзя быть уверенным, что процессы, поместившие элементы в очередь, завершатся. Также помните, что недемонические процессы присоединяются автоматически.
>
> Пример, который приведёт к взаимоблокировке:
>
> ```python
> from multiprocessing import Process, Queue
>
> def f(q):
>     q.put('X' * 1000000)
>
> if __name__ == '__main__':
>     queue = Queue()
>     p = Process(target=f, args=(queue,))
>     p.start()
>     p.join()                    # это приводит к взаимоблокировке
>     obj = queue.get()
> ```
>
> Исправление заключается в замене двух последних строк местами (или просто удалении строки с `p.join()`).

Явная передача ресурсов дочерним процессам

> В Unix дочерний процесс может использовать общий ресурс, созданный в родительском процессе, с помощью глобального ресурса. Однако лучше передавать объект в качестве аргумента конструктору дочернего процесса.
>
> Помимо обеспечения (потенциальной) совместимости с Windows, это также гарантирует, что пока дочерний процесс жив, объект не будет собран сборщиком мусора в родительском процессе. Это может быть важно, если какой-либо ресурс освобождается при сборке мусора в родительском процессе.
>
> Так, например
>
> ```python
> from multiprocessing import Process, Lock
>
> def f():
>     ... do something using "lock" ...
>
> if __name__ == '__main__':
>     lock = Lock()
>     for i in range(10):
>         Process(target=f).start()
> ```
>
> следует переписать как
>
> ```python
> from multiprocessing import Process, Lock
>
> def f(l):
>     ... do something using "l" ...
>
> if __name__ == '__main__':
>     lock = Lock()
>     for i in range(10):
>         Process(target=f, args=(lock,)).start()
> ```

Остерегайтесь замены [`sys.stdin`](https://python-all.ru/2.7/library/sys.html#sys.stdin) на «файлоподобный объект»

> [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) изначально безусловно вызывал:
>
> ```python
> os.close(sys.stdin.fileno())
> ```
>
> в методе `multiprocessing.Process._bootstrap()` – это приводило к проблемам с процессами внутри процессов. Теперь это изменено на:
>
> ```python
> sys.stdin.close()
> sys.stdin = open(os.devnull)
> ```
>
> Это решает основную проблему столкновения процессов друг с другом, приводящую к ошибке неверного файлового дескриптора, но вносит потенциальную опасность для приложений, которые заменяют [`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) для этого файлоподобного объекта, одни и те же данные могут быть сброшены в объект несколько раз, что приведёт к повреждению.
>
> Если вы пишете файлоподобный объект и реализуете собственное кэширование, вы можете сделать его устойчивым к fork, сохраняя pid при каждом добавлении в кэш и сбрасывая кэш при изменении pid. Например:
>
> ```python
> @property
> def cache(self):
>     pid = os.getpid()
>     if pid != self._pid:
>         self._pid = pid
>         self._cache = []
>     return self._cache
> ```
>
> Для получения дополнительной информации см. [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)

### 16.6.3.2. Windows

Поскольку в Windows отсутствует [`os.fork()`](https://python-all.ru/2.7/library/os.html#os.fork), у него есть несколько дополнительных ограничений:

Упаковываемость

> Убедитесь, что все аргументы `Process.__init__()` сериализуемы (picklable). Это означает, в частности, что связанные или несвязанные методы не могут использоваться напрямую в качестве аргумента `target` в Windows – просто определите функцию и используйте её вместо этого.
>
> Кроме того, если вы создаёте подкласс [`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).

Глобальные переменные

> Имейте в виду, что если код, выполняемый в дочернем процессе, попытается обратиться к глобальной переменной, то значение, которое он увидит (если оно есть), может не совпадать со значением в родительском процессе на момент вызова [`Process.start`](https://python-all.ru/2.7/library/multiprocessing.html#multiprocessing.Process.start).
>
> Однако глобальные переменные, которые представляют собой просто константы уровня модуля, не вызывают проблем.

Безопасный импорт главного модуля

> Необходимо убедиться, что главный модуль может быть безопасно импортирован новым интерпретатором Python без непреднамеренных побочных эффектов (например, запуска нового процесса).
>
> Например, в Windows при запуске следующего модуля возникнет ошибка [`RuntimeError`](https://python-all.ru/2.7/library/exceptions.html#exceptions.RuntimeError):
>
> ```python
> from multiprocessing import Process
>
> def foo():
>     print 'hello'
>
> p = Process(target=foo)
> p.start()
> ```
>
> Вместо этого следует защитить «точку входа» программы с помощью `if __name__ == '__main__':` следующим образом:
>
> ```python
> from multiprocessing import Process, freeze_support
>
> def foo():
>     print 'hello'
>
> if __name__ == '__main__':
>     freeze_support()
>     p = Process(target=foo)
>     p.start()
> ```
>
> (Строку `freeze_support()` можно опустить, если программа будет запускаться обычным образом, а не как замороженное приложение.)
>
> Это позволяет только что порождённому интерпретатору Python безопасно импортировать модуль и затем выполнить функцию `foo()` этого модуля.
>
> Аналогичные ограничения действуют, если пул или менеджер создаются в главном модуле.

## 16.6.4. Примеры

Демонстрация создания и использования настраиваемых менеджеров и прокси:

```python
#
# Этот модуль показывает, как использовать произвольные вызываемые объекты с подклассом `BaseManager`.
# `BaseManager`.
#
# Авторское право (c) 2006-2008, R Oudkerk
# Все права защищены.
#

from multiprocessing import freeze_support
from multiprocessing.managers import BaseManager, BaseProxy
import operator

##

class Foo(object):
    def f(self):
        print 'you called Foo.f()'
    def g(self):
        print 'you called Foo.g()'
    def _h(self):
        print 'you called Foo._h()'

# Простая функция-генератор
def baz():
    for i in xrange(10):
        yield i*i

# Тип прокси для объектов-генераторов
class GeneratorProxy(BaseProxy):
    _exposed_ = ('next', '__next__')
    def __iter__(self):
        return self
    def next(self):
        return self._callmethod('next')
    def __next__(self):
        return self._callmethod('__next__')

# Функция для возврата модуля operator
def get_operator_module():
    return operator

##

class MyManager(BaseManager):
    pass

# зарегистрировать класс Foo; сделать `f()` и `g()` доступными через прокси
MyManager.register('Foo1', Foo)

# зарегистрировать класс Foo; сделать `g()` и `_h()` доступными через прокси
MyManager.register('Foo2', Foo, exposed=('g', '_h'))

# зарегистрировать функцию-генератор baz; использовать `GeneratorProxy` для создания прокси
MyManager.register('baz', baz, proxytype=GeneratorProxy)

# зарегистрировать get_operator_module(); сделать публичные функции доступными через прокси
MyManager.register('operator', get_operator_module)

##

def test():
    manager = MyManager()
    manager.start()

    print '-' * 20

    f1 = manager.Foo1()
    f1.f()
    f1.g()
    assert not hasattr(f1, '_h')
    assert sorted(f1._exposed_) == sorted(['f', 'g'])

    print '-' * 20

    f2 = manager.Foo2()
    f2.g()
    f2._h()
    assert not hasattr(f2, 'f')
    assert sorted(f2._exposed_) == sorted(['g', '_h'])

    print '-' * 20

    it = manager.baz()
    for i in it:
        print '<%d>' % i,
    print

    print '-' * 20

    op = manager.operator()
    print 'op.add(23, 45) =', op.add(23, 45)
    print 'op.pow(2, 94) =', op.pow(2, 94)
    print 'op.getslice(range(10), 2, 6) =', op.getslice(range(10), 2, 6)
    print 'op.repeat(range(5), 3) =', op.repeat(range(5), 3)
    print 'op._exposed_ =', op._exposed_

##

if __name__ == '__main__':
    freeze_support()
    test()
```

Использование `Pool`:

```python
#
# Тест класса `multiprocessing.Pool`
#
# Авторское право (c) 2006-2008, R Oudkerk
# Все права защищены.
#

import multiprocessing
import time
import random
import sys

#
# Функции, используемые тестовым кодом
#

def calculate(func, args):
    result = func(*args)
    return '%s says that %s%s = %s' % (
        multiprocessing.current_process().name,
        func.__name__, args, result
        )

def calculatestar(args):
    return calculate(*args)

def mul(a, b):
    time.sleep(0.5*random.random())
    return a * b

def plus(a, b):
    time.sleep(0.5*random.random())
    return a + b

def f(x):
    return 1.0 / (x-5.0)

def pow3(x):
    return x**3

def noop(x):
    pass

#
# Тестовый код
#

def test():
    print 'cpu_count() = %d\n' % multiprocessing.cpu_count()

    #
    # Создать пул
    #

    PROCESSES = 4
    print 'Creating pool with %d processes\n' % PROCESSES
    pool = multiprocessing.Pool(PROCESSES)
    print 'pool = %s' % pool
    print

    #
    # Тесты
    #

    TASKS = [(mul, (i, 7)) for i in range(10)] + \
            [(plus, (i, 8)) for i in range(10)]

    results = [pool.apply_async(calculate, t) for t in TASKS]
    imap_it = pool.imap(calculatestar, TASKS)
    imap_unordered_it = pool.imap_unordered(calculatestar, TASKS)

    print 'Ordered results using pool.apply_async():'
    for r in results:
        print '\t', r.get()
    print

    print 'Ordered results using pool.imap():'
    for x in imap_it:
        print '\t', x
    print

    print 'Unordered results using pool.imap_unordered():'
    for x in imap_unordered_it:
        print '\t', x
    print

    print 'Ordered results using pool.map() --- will block till complete:'
    for x in pool.map(calculatestar, TASKS):
        print '\t', x
    print

    #
    # Простые тесты производительности
    #

    N = 100000
    print 'def pow3(x): return x**3'

    t = time.time()
    A = map(pow3, xrange(N))
    print '\tmap(pow3, xrange(%d)):\n\t\t%s seconds' % \
          (N, time.time() - t)

    t = time.time()
    B = pool.map(pow3, xrange(N))
    print '\tpool.map(pow3, xrange(%d)):\n\t\t%s seconds' % \
          (N, time.time() - t)

    t = time.time()
    C = list(pool.imap(pow3, xrange(N), chunksize=N//8))
    print '\tlist(pool.imap(pow3, xrange(%d), chunksize=%d)):\n\t\t%s' \
          ' seconds' % (N, N//8, time.time() - t)

    assert A == B == C, (len(A), len(B), len(C))
    print

    L = [None] * 1000000
    print 'def noop(x): pass'
    print 'L = [None] * 1000000'

    t = time.time()
    A = map(noop, L)
    print '\tmap(noop, L):\n\t\t%s seconds' % \
          (time.time() - t)

    t = time.time()
    B = pool.map(noop, L)
    print '\tpool.map(noop, L):\n\t\t%s seconds' % \
          (time.time() - t)

    t = time.time()
    C = list(pool.imap(noop, L, chunksize=len(L)//8))
    print '\tlist(pool.imap(noop, L, chunksize=%d)):\n\t\t%s seconds' % \
          (len(L)//8, time.time() - t)

    assert A == B == C, (len(A), len(B), len(C))
    print

    del A, B, C, L

    #
    # Тестирование обработки ошибок
    #

    print 'Testing error handling:'

    try:
        print pool.apply(f, (5,))
    except ZeroDivisionError:
        print '\tGot ZeroDivisionError as expected from pool.apply()'
    else:
        raise AssertionError('expected ZeroDivisionError')

    try:
        print pool.map(f, range(10))
    except ZeroDivisionError:
        print '\tGot ZeroDivisionError as expected from pool.map()'
    else:
        raise AssertionError('expected ZeroDivisionError')

    try:
        print list(pool.imap(f, range(10)))
    except ZeroDivisionError:
        print '\tGot ZeroDivisionError as expected from list(pool.imap())'
    else:
        raise AssertionError('expected ZeroDivisionError')

    it = pool.imap(f, range(10))
    for i in range(10):
        try:
            x = it.next()
        except ZeroDivisionError:
            if i == 5:
                pass
        except StopIteration:
            break
        else:
            if i == 5:
                raise AssertionError('expected ZeroDivisionError')

    assert i == 9
    print '\tGot ZeroDivisionError as expected from IMapIterator.next()'
    print

    #
    # Тестирование тайм-аутов
    #

    print 'Testing ApplyResult.get() with timeout:',
    res = pool.apply_async(calculate, TASKS[0])
    while 1:
        sys.stdout.flush()
        try:
            sys.stdout.write('\n\t%s' % res.get(0.02))
            break
        except multiprocessing.TimeoutError:
            sys.stdout.write('.')
    print
    print

    print 'Testing IMapIterator.next() with timeout:',
    it = pool.imap(calculatestar, TASKS)
    while 1:
        sys.stdout.flush()
        try:
            sys.stdout.write('\n\t%s' % it.next(0.02))
        except StopIteration:
            break
        except multiprocessing.TimeoutError:
            sys.stdout.write('.')
    print
    print

    #
    # Тестирование колбэка
    #

    print 'Testing callback:'

    A = []
    B = [56, 0, 1, 8, 27, 64, 125, 216, 343, 512, 729]

    r = pool.apply_async(mul, (7, 8), callback=A.append)
    r.wait()

    r = pool.map_async(pow3, range(10), callback=A.extend)
    r.wait()

    if A == B:
        print '\tcallbacks succeeded\n'
    else:
        print '\t*** callbacks failed\n\t\t%s != %s\n' % (A, B)

    #
    # Проверить, что нет незавершённых задач
    #

    assert not pool._cache, 'cache = %r' % pool._cache

    #
    # Проверить методы close()
    #

    print 'Testing close():'

    for worker in pool._pool:
        assert worker.is_alive()

    result = pool.apply_async(time.sleep, [0.5])
    pool.close()
    pool.join()

    assert result.get() is None

    for worker in pool._pool:
        assert not worker.is_alive()

    print '\tclose() succeeded\n'

    #
    # Проверить метод terminate()
    #

    print 'Testing terminate():'

    pool = multiprocessing.Pool(2)
    DELTA = 0.1
    ignore = pool.apply(pow3, [2])
    results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]
    pool.terminate()
    pool.join()

    for worker in pool._pool:
        assert not worker.is_alive()

    print '\tterminate() succeeded\n'

    #
    # Проверить сборку мусора
    #

    print 'Testing garbage collection:'

    pool = multiprocessing.Pool(2)
    DELTA = 0.1
    processes = pool._pool
    ignore = pool.apply(pow3, [2])
    results = [pool.apply_async(time.sleep, [DELTA]) for i in range(100)]

    results = pool = None

    time.sleep(DELTA * 2)

    for worker in processes:
        assert not worker.is_alive()

    print '\tgarbage collection succeeded\n'

if __name__ == '__main__':
    multiprocessing.freeze_support()

    assert len(sys.argv) in (1, 2)

    if len(sys.argv) == 1 or sys.argv[1] == 'processes':
        print ' Using processes '.center(79, '-')
    elif sys.argv[1] == 'threads':
        print ' Using threads '.center(79, '-')
        import multiprocessing.dummy as multiprocessing
    else:
        print 'Usage:\n\t%s [processes | threads]' % sys.argv[0]
        raise SystemExit(2)

    test()
```

Типы синхронизации, такие как блокировки, условия и очереди:

```python
#
# Тестовый файл для пакета `multiprocessing`
#
# Авторское право (c) 2006-2008, R Oudkerk
# Все права защищены.
#

import time, sys, random
from Queue import Empty

import multiprocessing               # может быть перезаписан

#### TEST_VALUE

def value_func(running, mutex):
    random.seed()
    time.sleep(random.random()*4)

    mutex.acquire()
    print '\n\t\t\t' + str(multiprocessing.current_process()) + ' has finished'
    running.value -= 1
    mutex.release()

def test_value():
    TASKS = 10
    running = multiprocessing.Value('i', TASKS)
    mutex = multiprocessing.Lock()

    for i in range(TASKS):
        p = multiprocessing.Process(target=value_func, args=(running, mutex))
        p.start()

    while running.value > 0:
        time.sleep(0.08)
        mutex.acquire()
        print running.value,
        sys.stdout.flush()
        mutex.release()

    print
    print 'No more running processes'

#### TEST_QUEUE

def queue_func(queue):
    for i in range(30):
        time.sleep(0.5 * random.random())
        queue.put(i*i)
    queue.put('STOP')

def test_queue():
    q = multiprocessing.Queue()

    p = multiprocessing.Process(target=queue_func, args=(q,))
    p.start()

    o = None
    while o != 'STOP':
        try:
            o = q.get(timeout=0.3)
            print o,
            sys.stdout.flush()
        except Empty:
            print 'TIMEOUT'

    print

#### TEST_CONDITION

def condition_func(cond):
    cond.acquire()
    print '\t' + str(cond)
    time.sleep(2)
    print '\tchild is notifying'
    print '\t' + str(cond)
    cond.notify()
    cond.release()

def test_condition():
    cond = multiprocessing.Condition()

    p = multiprocessing.Process(target=condition_func, args=(cond,))
    print cond

    cond.acquire()
    print cond
    cond.acquire()
    print cond

    p.start()

    print 'main is waiting'
    cond.wait()
    print 'main has woken up'

    print cond
    cond.release()
    print cond
    cond.release()

    p.join()
    print cond

#### TEST_SEMAPHORE

def semaphore_func(sema, mutex, running):
    sema.acquire()

    mutex.acquire()
    running.value += 1
    print running.value, 'tasks are running'
    mutex.release()

    random.seed()
    time.sleep(random.random()*2)

    mutex.acquire()
    running.value -= 1
    print '%s has finished' % multiprocessing.current_process()
    mutex.release()

    sema.release()

def test_semaphore():
    sema = multiprocessing.Semaphore(3)
    mutex = multiprocessing.RLock()
    running = multiprocessing.Value('i', 0)

    processes = [
        multiprocessing.Process(target=semaphore_func,
                                args=(sema, mutex, running))
        for i in range(10)
        ]

    for p in processes:
        p.start()

    for p in processes:
        p.join()

#### TEST_JOIN_TIMEOUT

def join_timeout_func():
    print '\tchild sleeping'
    time.sleep(5.5)
    print '\n\tchild terminating'

def test_join_timeout():
    p = multiprocessing.Process(target=join_timeout_func)
    p.start()

    print 'waiting for process to finish'

    while 1:
        p.join(timeout=1)
        if not p.is_alive():
            break
        print '.',
        sys.stdout.flush()

#### TEST_EVENT

def event_func(event):
    print '\t%r is waiting' % multiprocessing.current_process()
    event.wait()
    print '\t%r has woken up' % multiprocessing.current_process()

def test_event():
    event = multiprocessing.Event()

    processes = [multiprocessing.Process(target=event_func, args=(event,))
                 for i in range(5)]

    for p in processes:
        p.start()

    print 'main is sleeping'
    time.sleep(2)

    print 'main is setting event'
    event.set()

    for p in processes:
        p.join()

#### TEST_SHAREDVALUES

def sharedvalues_func(values, arrays, shared_values, shared_arrays):
    for i in range(len(values)):
        v = values[i][1]
        sv = shared_values[i].value
        assert v == sv

    for i in range(len(values)):
        a = arrays[i][1]
        sa = list(shared_arrays[i][:])
        assert a == sa

    print 'Tests passed'

def test_sharedvalues():
    values = [
        ('i', 10),
        ('h', -2),
        ('d', 1.25)
        ]
    arrays = [
        ('i', range(100)),
        ('d', [0.25 * i for i in range(100)]),
        ('H', range(1000))
        ]

    shared_values = [multiprocessing.Value(id, v) for id, v in values]
    shared_arrays = [multiprocessing.Array(id, a) for id, a in arrays]

    p = multiprocessing.Process(
        target=sharedvalues_func,
        args=(values, arrays, shared_values, shared_arrays)
        )
    p.start()
    p.join()

    assert p.exitcode == 0

####

def test(namespace=multiprocessing):
    global multiprocessing

    multiprocessing = namespace

    for func in [ test_value, test_queue, test_condition,
                  test_semaphore, test_join_timeout, test_event,
                  test_sharedvalues ]:

        print '\n\t######## %s\n' % func.__name__
        func()

    ignore = multiprocessing.active_children()      # очистка всех старых процессов
    if hasattr(multiprocessing, '_debug_info'):
        info = multiprocessing._debug_info()
        if info:
            print info
            raise ValueError('there should be no positive refcounts left')

if __name__ == '__main__':
    multiprocessing.freeze_support()

    assert len(sys.argv) in (1, 2)

    if len(sys.argv) == 1 or sys.argv[1] == 'processes':
        print ' Using processes '.center(79, '-')
        namespace = multiprocessing
    elif sys.argv[1] == 'manager':
        print ' Using processes and a manager '.center(79, '-')
        namespace = multiprocessing.Manager()
        namespace.Process = multiprocessing.Process
        namespace.current_process = multiprocessing.current_process
        namespace.active_children = multiprocessing.active_children
    elif sys.argv[1] == 'threads':
        print ' Using threads '.center(79, '-')
        import multiprocessing.dummy as namespace
    else:
        print 'Usage:\n\t%s [processes | manager | threads]' % sys.argv[0]
        raise SystemExit(2)

    test(namespace)
```

Пример использования очередей для передачи задач набору рабочих процессов и сбора результатов:

```python
#
# Простой пример использования пула рабочих процессов для выполнения задач.
#
# Обратите внимание, что результаты, скорее всего, не будут получены из выходной
# очереди в том же порядке, в котором соответствующие задачи были
# помещены во входную очередь. Если важно получить результаты
# в исходном порядке, рассмотрите использование `Pool.map()` или
# `Pool.imap()` (что в любом случае сократит объём необходимого кода).
#
# Авторское право (c) 2006-2008, R Oudkerk
# Все права защищены.
#

import time
import random

from multiprocessing import Process, Queue, current_process, freeze_support

#
# Функция, выполняемая рабочими процессами
#

def worker(input, output):
    for func, args in iter(input.get, 'STOP'):
        result = calculate(func, args)
        output.put(result)

#
# Функция, используемая для вычисления результата
#

def calculate(func, args):
    result = func(*args)
    return '%s says that %s%s = %s' % \
        (current_process().name, func.__name__, args, result)

#
# Функции, на которые ссылаются задачи
#

def mul(a, b):
    time.sleep(0.5*random.random())
    return a * b

def plus(a, b):
    time.sleep(0.5*random.random())
    return a + b

#
#
#

def test():
    NUMBER_OF_PROCESSES = 4
    TASKS1 = [(mul, (i, 7)) for i in range(20)]
    TASKS2 = [(plus, (i, 8)) for i in range(10)]

    # Создание очередей
    task_queue = Queue()
    done_queue = Queue()

    # Отправка задач
    for task in TASKS1:
        task_queue.put(task)

    # Запуск рабочих процессов
    for i in range(NUMBER_OF_PROCESSES):
        Process(target=worker, args=(task_queue, done_queue)).start()

    # Получение и вывод результатов
    print 'Unordered results:'
    for i in range(len(TASKS1)):
        print '\t', done_queue.get()

    # Добавить ещё задач с помощью `put()`
    for task in TASKS2:
        task_queue.put(task)

    # Получить и вывести ещё несколько результатов
    for i in range(len(TASKS2)):
        print '\t', done_queue.get()

    # Сообщить дочерним процессам об остановке
    for i in range(NUMBER_OF_PROCESSES):
        task_queue.put('STOP')

if __name__ == '__main__':
    freeze_support()
    test()
```

Пример того, как пул рабочих процессов может запускать экземпляр `SimpleHTTPServer.HttpServer`, используя один общий прослушивающий сокет.

```python
#
# Пример, в котором пул HTTP-серверов использует один общий слушающий сокет
#
# В Windows этот модуль зависит от возможности сериализации сокета с помощью pickle
# объекта, чтобы рабочие процессы могли унаследовать копию сервера
# объект. (Мы импортируем `multiprocessing.reduction`, чтобы включить эту сериализацию.)
#
# Не уверены, нужно ли синхронизировать доступ к методу `socket.accept()` с помощью
# блокировки, разделяемой между процессами – похоже, это не обязательно.
#
# Авторское право (c) 2006-2008, R Oudkerk
# Все права защищены.
#

import os
import sys

from multiprocessing import Process, current_process, freeze_support
from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler

if sys.platform == 'win32':
    import multiprocessing.reduction    # сделать сокеты сериализуемыми/наследуемыми

def note(format, *args):
    sys.stderr.write('[%s]\t%s\n' % (current_process().name, format%args))

class RequestHandler(SimpleHTTPRequestHandler):
    # мы переопределяем log_message(), чтобы показывать, какой процесс обрабатывает запрос
    def log_message(self, format, *args):
        note(format, *args)

def serve_forever(server):
    note('starting server')
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass

def runpool(address, number_of_processes):
    # создать единственный объект сервера – каждый дочерний процесс унаследует его копию
    server = HTTPServer(address, RequestHandler)

    # создать дочерние процессы, которые будут работать как воркеры
    for i in range(number_of_processes-1):
        Process(target=serve_forever, args=(server,)).start()

    # главный процесс также выступает в роли воркера
    serve_forever(server)

def test():
    DIR = os.path.join(os.path.dirname(__file__), '..')
    ADDRESS = ('localhost', 8000)
    NUMBER_OF_PROCESSES = 4

    print 'Serving at http://%s:%d using %d worker processes' % \
          (ADDRESS[0], ADDRESS[1], NUMBER_OF_PROCESSES)
    print 'To exit press Ctrl-' + ['C', 'Break'][sys.platform=='win32']

    os.chdir(DIR)
    runpool(ADDRESS, NUMBER_OF_PROCESSES)

if __name__ == '__main__':
    freeze_support()
    test()
```

Несколько простых тестов, сравнивающих [`multiprocessing`](https://python-all.ru/2.7/library/multiprocessing.html#module-multiprocessing) с [`threading`](https://python-all.ru/2.7/library/threading.html#module-threading):

```python
#
# Простые тесты производительности для пакета multiprocessing
#
# Авторское право (c) 2006-2008, R Oudkerk
# Все права защищены.
#

import time, sys, multiprocessing, threading, Queue, gc

if sys.platform == 'win32':
    _timer = time.clock
else:
    _timer = time.time

delta = 1

#### TEST_QUEUESPEED

def queuespeed_func(q, c, iterations):
    a = '0' * 256
    c.acquire()
    c.notify()
    c.release()

    for i in xrange(iterations):
        q.put(a)

    q.put('STOP')

def test_queuespeed(Process, q, c):
    elapsed = 0
    iterations = 1

    while elapsed < delta:
        iterations *= 2

        p = Process(target=queuespeed_func, args=(q, c, iterations))
        c.acquire()
        p.start()
        c.wait()
        c.release()

        result = None
        t = _timer()

        while result != 'STOP':
            result = q.get()

        elapsed = _timer() - t

        p.join()

    print iterations, 'objects passed through the queue in', elapsed, 'seconds'
    print 'average number/sec:', iterations/elapsed

#### TEST_PIPESPEED

def pipe_func(c, cond, iterations):
    a = '0' * 256
    cond.acquire()
    cond.notify()
    cond.release()

    for i in xrange(iterations):
        c.send(a)

    c.send('STOP')

def test_pipespeed():
    c, d = multiprocessing.Pipe()
    cond = multiprocessing.Condition()
    elapsed = 0
    iterations = 1

    while elapsed < delta:
        iterations *= 2

        p = multiprocessing.Process(target=pipe_func,
                                    args=(d, cond, iterations))
        cond.acquire()
        p.start()
        cond.wait()
        cond.release()

        result = None
        t = _timer()

        while result != 'STOP':
            result = c.recv()

        elapsed = _timer() - t
        p.join()

    print iterations, 'objects passed through connection in',elapsed,'seconds'
    print 'average number/sec:', iterations/elapsed

#### TEST_SEQSPEED

def test_seqspeed(seq):
    elapsed = 0
    iterations = 1

    while elapsed < delta:
        iterations *= 2

        t = _timer()

        for i in xrange(iterations):
            a = seq[5]

        elapsed = _timer()-t

    print iterations, 'iterations in', elapsed, 'seconds'
    print 'average number/sec:', iterations/elapsed

#### TEST_LOCK

def test_lockspeed(l):
    elapsed = 0
    iterations = 1

    while elapsed < delta:
        iterations *= 2

        t = _timer()

        for i in xrange(iterations):
            l.acquire()
            l.release()

        elapsed = _timer()-t

    print iterations, 'iterations in', elapsed, 'seconds'
    print 'average number/sec:', iterations/elapsed

#### TEST_CONDITION

def conditionspeed_func(c, N):
    c.acquire()
    c.notify()

    for i in xrange(N):
        c.wait()
        c.notify()

    c.release()

def test_conditionspeed(Process, c):
    elapsed = 0
    iterations = 1

    while elapsed < delta:
        iterations *= 2

        c.acquire()
        p = Process(target=conditionspeed_func, args=(c, iterations))
        p.start()

        c.wait()

        t = _timer()

        for i in xrange(iterations):
            c.notify()
            c.wait()

        elapsed = _timer()-t

        c.release()
        p.join()

    print iterations * 2, 'waits in', elapsed, 'seconds'
    print 'average number/sec:', iterations * 2 / elapsed

####

def test():
    manager = multiprocessing.Manager()

    gc.disable()

    print '\n\t######## testing Queue.Queue\n'
    test_queuespeed(threading.Thread, Queue.Queue(),
                    threading.Condition())
    print '\n\t######## testing multiprocessing.Queue\n'
    test_queuespeed(multiprocessing.Process, multiprocessing.Queue(),
                    multiprocessing.Condition())
    print '\n\t######## testing Queue managed by server process\n'
    test_queuespeed(multiprocessing.Process, manager.Queue(),
                    manager.Condition())
    print '\n\t######## testing multiprocessing.Pipe\n'
    test_pipespeed()

    print

    print '\n\t######## testing list\n'
    test_seqspeed(range(10))
    print '\n\t######## testing list managed by server process\n'
    test_seqspeed(manager.list(range(10)))
    print '\n\t######## testing Array("i", ..., lock=False)\n'
    test_seqspeed(multiprocessing.Array('i', range(10), lock=False))
    print '\n\t######## testing Array("i", ..., lock=True)\n'
    test_seqspeed(multiprocessing.Array('i', range(10), lock=True))

    print

    print '\n\t######## testing threading.Lock\n'
    test_lockspeed(threading.Lock())
    print '\n\t######## testing threading.RLock\n'
    test_lockspeed(threading.RLock())
    print '\n\t######## testing multiprocessing.Lock\n'
    test_lockspeed(multiprocessing.Lock())
    print '\n\t######## testing multiprocessing.RLock\n'
    test_lockspeed(multiprocessing.RLock())
    print '\n\t######## testing lock managed by server process\n'
    test_lockspeed(manager.Lock())
    print '\n\t######## testing rlock managed by server process\n'
    test_lockspeed(manager.RLock())

    print

    print '\n\t######## testing threading.Condition\n'
    test_conditionspeed(threading.Thread, threading.Condition())
    print '\n\t######## testing multiprocessing.Condition\n'
    test_conditionspeed(multiprocessing.Process, multiprocessing.Condition())
    print '\n\t######## testing condition managed by a server process\n'
    test_conditionspeed(multiprocessing.Process, manager.Condition())

    gc.enable()

if __name__ == '__main__':
    multiprocessing.freeze_support()
    test()
```
