multiprocessing.shared_memory — Предоставляет общую память для прямого доступа между процессами

Добавлено в версии 3.8.


Данный модуль предоставляет класс SharedMemory для выделения и управления разделяемой памятью, доступной одному или нескольким процессам на многоядерном или симметричном многопроцессорном (SMP) компьютере. Чтобы помочь в управлении жизненным циклом общей памяти, особенно в отдельных процессах, в модуле multiprocessing.managers также предусмотрен подкласс BaseManager класса SharedMemoryManager.

В этом модуле общая память относится к блокам разделяемой памяти в стиле System V (хотя это не обязательно реализовано явно как таковое) и не относится к «распределённой разделяемой памяти». Данный стиль разделяемой памяти позволяет различным процессам потенциально читать и записывать в общую (или совместно используемую) область энергозависимой памяти. Процессы обычно ограничены доступом только к своему собственному пространству памяти процесса, но общая память позволяет обмениваться данными между процессами, избегая вместо этого необходимости отправлять сообщения с данными между процессами. Совместное использование данных непосредственно через память может обеспечить значительные преимущества в производительности по сравнению с обменом данными через диск или сокет или другими средствами связи, требующими сериализации/десериализации и копирования данных.

class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0)

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

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

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

create определяет, будет ли создан новый блок общей памяти (True) или будет присоединен существующий блок общей памяти (False).

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

close()

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

Запрашивает уничтожение базового блока разделяемой памяти. Чтобы обеспечить надлежащую очистку ресурсов, unlink() следует вызывать один раз (и только один раз) для всех процессов, которым требуется блок общей памяти. После запроса на его уничтожение блок общей памяти может быть немедленно уничтожен, а может и нет, и это поведение может различаться на разных платформах. Попытки доступа к данным внутри блока общей памяти после вызова unlink() могут привести к ошибкам доступа к памяти. Примечание: последний процесс, освобождающий блок общей памяти, может вызывать unlink() и close() в любом порядке.

buf

Представление памяти содержимого блока общей памяти.

name

Доступ только для чтения к уникальному имени блока общей памяти.

size

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

В следующем примере демонстрируется низкоуровневое использование экземпляров SharedMemory:

>>> from multiprocessing import shared_memory
>>> shm_a = shared_memory.SharedMemory(create=True, size=10)
>>> type(shm_a.buf)
<class 'memoryview'>
>>> buffer = shm_a.buf
>>> len(buffer)
10
>>> buffer[:4] = bytearray([22, 33, 44, 55])  # Изменяем сразу несколько
>>> buffer[4] = 100                           # Изменяем по одному байту за раз
>>> # Присоединяемся к существующему блоку общей памяти
>>> shm_b = shared_memory.SharedMemory(shm_a.name)
>>> import array
>>> array.array('b', shm_b.buf[:5])  # Копия данных в новый array.array
array('b', [22, 33, 44, 55, 100])
>>> shm_b.buf[:5] = b'привет'  # Изменен через shm_b, используя байты
>>> bytes(shm_a.buf[:5])      # Доступ через shm_a
b'привет'
>>> shm_b.close()   # Закрываем каждый экземпляр SharedMemory
>>> shm_a.close()
>>> shm_a.unlink()  # Вызов unlink только один раз, чтобы освободить общую память

В следующем примере показано практическое использование класса SharedMemory с NumPy массивами, доступ к одному и тому же numpy.ndarray из двух разных оболочек Python:

>>> # В первой интерактивной оболочке Python
>>> import numpy as np
>>> a = np.array([1, 1, 2, 3, 5, 8])  # Начало с существующего массива NumPy
>>> from multiprocessing import shared_memory
>>> shm = shared_memory.SharedMemory(create=True, size=a.nbytes)
>>> # Теперь создадим массив NumPy, поддерживаемый общей памятью.
>>> b = np.ndarray(a.shape, dtype=a.dtype, buffer=shm.buf)
>>> b[:] = a[:]  # Копируем исходные данные в общую память
>>> b
array([1, 1, 2, 3, 5, 8])
>>> type(b)
<class 'numpy.ndarray'>
>>> type(a)
<class 'numpy.ndarray'>
>>> shm.name  # Мы не указывали имя, поэтому его выбрали за нас
'psm_21467_46075'

>>> # В той же оболочке или в новой оболочке Python на том же компьютере.
>>> import numpy as np
>>> from multiprocessing import shared_memory
>>> # Присоединяемся к существующему блоку общей памяти
>>> existing_shm = shared_memory.SharedMemory(name='psm_21467_46075')
>>> # Обратите внимание, что в этом примере a.shape — (6,), а a.dtype — np.int64.
>>> c = np.ndarray((6,), dtype=np.int64, buffer=existing_shm.buf)
>>> c
array([1, 1, 2, 3, 5, 8])
>>> c[-1] = 888
>>> c
array([  1,   1,   2,   3,   5, 888])

>>> # Вернувшись в первую интерактивную оболочку Python, b отражает это изменение.
>>> b
array([  1,   1,   2,   3,   5, 888])

>>> # Очистка из второй оболочки Python
>>> del c  # Ненужно; просто подчеркивание массива больше не используется
>>> existing_shm.close()

>>> # Очистка из первой оболочки Python
>>> del b  # Ненужно; просто подчеркивание массива больше не используется
>>> shm.close()
>>> shm.unlink()  # Очистка и освобождение блока общей памяти в самом конце
class multiprocessing.managers.SharedMemoryManager([address[, authkey]])

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

Вызов start() в экземпляре SharedMemoryManager приводит к запуску нового процесса. Единственной целью нового процесса является управление жизненным циклом всех блоков разделяемой памяти, созданных с его помощью. Чтобы инициировать освобождение всех блоков общей памяти, управляемых этим процессом, вызовите shutdown() на экземпляре. Это инициирует вызов SharedMemory.unlink() для всех объектов SharedMemory, управляемых этим процессом, а затем останавливает сам процесс. Создавая экземпляры SharedMemory через SharedMemoryManager, мы избегаем необходимости вручную отслеживать и инициировать освобождение ресурсов общей памяти.

Данный класс предоставляет методы для создания и возврата экземпляров SharedMemory, а также для создания объекта, подобного списку (ShareableList), поддерживаемого общей памятью.

Обратитесь к multiprocessing.managers.BaseManager для описания унаследованных необязательных входных аргументов address и authkey и того, как их можно использовать для подключения к существующей службе SharedMemoryManager из других процессов.

SharedMemory(size)

Создаёт и возвращает новый объект SharedMemory с указанным size в байтах.

ShareableList(sequence)

Создаёт и возвращает новый объект ShareableList, инициализированный значениями из ввода sequence.

В следующем примере демонстрируются основные механизмы SharedMemoryManager:

>>> from multiprocessing.managers import SharedMemoryManager
>>> smm = SharedMemoryManager()
>>> smm.start()  # Запуск процесса, который управляет блоками общей памяти.
>>> sl = smm.ShareableList(range(4))
>>> sl
ShareableList([0, 1, 2, 3], name='psm_6572_7512')
>>> raw_shm = smm.SharedMemory(size=128)
>>> another_sl = smm.ShareableList('alpha')
>>> another_sl
ShareableList(['a', 'l', 'p', 'h', 'a'], name='psm_6572_12221')
>>> smm.shutdown()  # Вызывает unlink() для sl, raw_shm и other_sl

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

>>> with SharedMemoryManager() as smm:
...     sl = smm.ShareableList(range(2000))
...     # Разделить работу между двумя процессами, сохраняя частичные результаты в sl
...     p1 = Process(target=do_work, args=(sl, 0, 1000))
...     p2 = Process(target=do_work, args=(sl, 1000, 2000))
...     p1.start()
...     p2.start()  # multiprocessing.Pool может быть более эффективным
...     p1.join()
...     p2.join()   # Дождаться завершения всей работы в обоих процессах.
...     total_result = sum(sl)  # Консолидируем частичные результаты в sl

При использовании SharedMemoryManager в операторе with все блоки общей памяти, созданные с помощью этого менеджера, освобождаются, когда завершается выполнение блока кода оператора with.

class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)

Предоставляет изменяемый объект, похожий на список, в котором все значения хранятся в общем блоке памяти. Это ограничивает сохраняемые значения только встроенными типами данных int, float, bool, str (менее 10 М байт каждый), bytes (менее 10 МБ байт каждый) и None. Он также заметно отличается от встроенного типа list тем, что данные списки не могут изменять свою общую длину (т. е. не добавлять, не вставлять и т. д.) и не поддерживают динамическое создание новых экземпляров ShareableList посредством нарезки.

sequence используется для заполнения нового ShareableList, полного значений. Устанавливает None, чтобы вместо этого подключиться к уже существующему ShareableList по его уникальному имени в общей памяти.

name — это уникальное имя запрошенной общей памяти, как приведено в определении для SharedMemory. При присоединении к существующему ShareableList указать уникальное имя его блока общей памяти, оставив для sequence значение None.

count(value)

Возвращает количество вхождений value.

index(value)

Возвращает первую позицию индекса value. Вызывает ValueError, если value отсутствует.

format

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

shm

Экземпляр SharedMemory, в котором хранятся значения.

В следующем примере демонстрируется базовое использование экземпляра ShareableList:

>>> from multiprocessing import shared_memory
>>> a = shared_memory.ShareableList(['привет', b'ПРИВЕТ', -273.154, 100, None, True, 42])
>>> [ type(entry) for entry in a ]
[<class 'str'>, <class 'bytes'>, <class 'float'>, <class 'int'>, <class 'NoneType'>, <class 'bool'>, <class 'int'>]
>>> a[2]
-273.154
>>> a[2] = -78.5
>>> a[2]
-78.5
>>> a[2] = 'сухой лед'  # Изменение типов данных также поддерживается
>>> a[2]
'сухой лед'
>>> a[2] = 'больше, чем ранее выделенное пространство для хранения'
Traceback (most recent call last):
  ...
ValueError: exceeds available storage for existing str
>>> a[2]
'сухой лед'
>>> len(a)
7
>>> a.index(42)
6
>>> a.count(b'привет')
0
>>> a.count(b'ПРИВЕТ')
1
>>> a.shm.close()
>>> a.shm.unlink()
>>> del a  # Использование ShareableList после вызова unlink() не поддерживается.

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

>>> b = shared_memory.ShareableList(range(5))         # В первом процессе
>>> c = shared_memory.ShareableList(name=b.shm.name)  # Во втором процессе
>>> c
ShareableList([0, 1, 2, 3, 4], name='...')
>>> c[-1] = -999
>>> b[-1]
-999
>>> b.shm.close()
>>> c.shm.close()
>>> c.shm.unlink()