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

Исходный код: Lib/multiprocessing/shared_memory.py

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


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

В этом модуле совместно используемая память относится к блокам совместно используемой памяти «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

Запоминающее представление (memoryview) содержимого блока совместно используемой памяти.

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'howdy'  # Изменение через shm_b с использованием байтов
>>> bytes(shm_a.buf[:5])      # Доступ через shm_a
b'howdy'
>>> 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 is (6,) и a.dtype is 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 и another_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 (меньше, чем 10M байты каждый), bytes (меньше, чем 10M байты каждый), и встроенные типы данных 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(['howdy', b'HoWdY', -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] = 'dry ice'  # Также поддерживается изменение типов данных
>>> a[2]
'dry ice'
>>> a[2] = 'larger than previously allocated storage space'
Traceback (most recent call last):
  ...
ValueError: exceeds available storage for existing str
>>> a[2]
'dry ice'
>>> len(a)
7
>>> a.index(42)
6
>>> a.count(b'howdy')
0
>>> a.count(b'HoWdY')
1
>>> a.shm.close()
>>> a.shm.unlink()
>>> del a  # Использование ShareList после вызова команды unlink() не поддерживается

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

>>> 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()