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

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

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])  # Modify multiple at once
>>> buffer[4] = 100                           # Modify single byte at a time
>>> # Attach to an existing shared memory block
>>> shm_b = shared_memory.SharedMemory(shm_a.name)
>>> import array
>>> array.array('b', shm_b.buf[:5])  # Copy the data into a new array.array
array('b', [22, 33, 44, 55, 100])
>>> shm_b.buf[:5] = b'howdy'  # Modify via shm_b using bytes
>>> bytes(shm_a.buf[:5])      # Access via shm_a
b'howdy'
>>> shm_b.close()   # Close each SharedMemory instance
>>> shm_a.close()
>>> shm_a.unlink()  # Call unlink only once to release the shared memory

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

>>> # In the first Python interactive shell
>>> import numpy as np
>>> a = np.array([1, 1, 2, 3, 5, 8])  # Start with an existing NumPy array
>>> from multiprocessing import shared_memory
>>> shm = shared_memory.SharedMemory(create=True, size=a.nbytes)
>>> # Now create a NumPy array backed by shared memory
>>> b = np.ndarray(a.shape, dtype=a.dtype, buffer=shm.buf)
>>> b[:] = a[:]  # Copy the original data into shared memory
>>> b
array([1, 1, 2, 3, 5, 8])
>>> type(b)
<class 'numpy.ndarray'>
>>> type(a)
<class 'numpy.ndarray'>
>>> shm.name  # We did not specify a name so one was chosen for us
'psm_21467_46075'

>>> # In either the same shell or a new Python shell on the same machine
>>> import numpy as np
>>> from multiprocessing import shared_memory
>>> # Attach to the existing shared memory block
>>> existing_shm = shared_memory.SharedMemory(name='psm_21467_46075')
>>> # Note that a.shape is (6,) and a.dtype is np.int64 in this example
>>> 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])

>>> # Back in the first Python interactive shell, b reflects this change
>>> b
array([  1,   1,   2,   3,   5, 888])

>>> # Clean up from within the second Python shell
>>> del c  # Unnecessary; merely emphasizing the array is no longer used
>>> existing_shm.close()

>>> # Clean up from within the first Python shell
>>> del b  # Unnecessary; merely emphasizing the array is no longer used
>>> shm.close()
>>> shm.unlink()  # Free and release the shared memory block at the very end
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()  # Start the process that manages the shared memory blocks
>>> 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()  # Calls unlink() on sl, raw_shm, and another_sl

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

>>> with SharedMemoryManager() as smm:
...     sl = smm.ShareableList(range(2000))
...     # Divide the work among two processes, storing partial results in sl
...     p1 = Process(target=do_work, args=(sl, 0, 1000))
...     p2 = Process(target=do_work, args=(sl, 1000, 2000))
...     p1.start()
...     p2.start()  # A multiprocessing.Pool might be more efficient
...     p1.join()
...     p2.join()   # Wait for all work to complete in both processes
...     total_result = sum(sl)  # Consolidate the partial results now in sl

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

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

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

Последовательность используется при заполнении нового 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'  # Changing data types is supported as well
>>> 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  # Use of a ShareableList after call to unlink() is unsupported

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

>>> b = shared_memory.ShareableList(range(5))         # In a first process
>>> c = shared_memory.ShareableList(name=b.shm.name)  # In a second process
>>> c
ShareableList([0, 1, 2, 3, 4], name='...')
>>> c[-1] = -999
>>> b[-1]
-999
>>> b.shm.close()
>>> c.shm.close()
>>> c.shm.unlink()

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

>>> import pickle
>>> from multiprocessing import shared_memory
>>> sl = shared_memory.ShareableList(range(10))
>>> list(sl)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> deserialized_sl = pickle.loads(pickle.dumps(sl))
>>> list(deserialized_sl)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sl[0] = -1
>>> deserialized_sl[1] = -2
>>> list(sl)
[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list(deserialized_sl)
[-1, -2, 2, 3, 4, 5, 6, 7, 8, 9]
>>> sl.shm.close()
>>> sl.shm.unlink()
Вернуться на верх