multiprocessing.shared_memory — Разделяемая память для прямого доступа между процессами
Исходный код: Lib/multiprocessing/shared_memory.py
Добавлено в версии 3.8.
Этот модуль предоставляет класс SharedMemory для выделения и управления разделяемой памятью, к которой могут обращаться один или несколько процессов на многоядерном компьютере или компьютере с симметричной многопроцессорной архитектурой (SMP). Для упрощения управления жизненным циклом разделяемой памяти, особенно между разными процессами, в модуле multiprocessing.managers также предоставлен подкласс BaseManager — SharedMemoryManager.
В этом модуле под разделяемой памятью понимаются блоки разделяемой памяти «в стиле POSIX» (хотя они не обязательно реализованы именно таким образом); это понятие не относится к «распределённой разделяемой памяти». Такой вид разделяемой памяти потенциально позволяет разным процессам читать и записывать данные в общую область энергозависимой памяти. Обычно процессы могут обращаться только к собственной области памяти, однако разделяемая память позволяет обмениваться данными между процессами и не требует вместо этого отправлять друг другу сообщения с этими данными. Прямой обмен данными через память может обеспечить значительный выигрыш в производительности по сравнению с обменом через диск, сокет или другие каналы связи, требующие сериализации, десериализации и копирования данных.
-
Создаёт экземпляр класса
SharedMemoryдля создания нового блока разделяемой памяти или подключения к существующему блоку. Каждому блоку разделяемой памяти присваивается уникальное имя. Таким образом, один процесс может создать блок разделяемой памяти с определённым именем, а другой процесс — подключиться к этому же блоку, используя то же имя.Блоки разделяемой памяти, используемые для обмена данными между процессами, могут существовать дольше исходного процесса, который их создал. Когда процессу больше не нужен доступ к блоку разделяемой памяти, который ещё может потребоваться другим процессам, следует вызвать метод
close(). Когда блок разделяемой памяти больше не нужен ни одному процессу, следует вызвать методunlink(), чтобы обеспечить корректную очистку.- Параметры:
-
-
name (str | None) – Уникальное имя запрашиваемой разделяемой памяти, задаваемое в виде строки. При создании нового блока разделяемой памяти, если для имени указано
None(значение по умолчанию), будет сгенерировано новое имя. -
create (bool) – Определяет, следует ли создать новый блок разделяемой памяти (
True) или подключиться к существующему (False). - size (int) – Запрашиваемое количество байтов при создании нового блока разделяемой памяти. Поскольку некоторые платформы выделяют память блоками, размер которых определяется размером страницы памяти на этой платформе, фактический размер блока разделяемой памяти может быть больше или равен запрошенному. При подключении к существующему блоку разделяемой памяти параметр size игнорируется.
-
name (str | None) – Уникальное имя запрашиваемой разделяемой памяти, задаваемое в виде строки. При создании нового блока разделяемой памяти, если для имени указано
-
Закрывает доступ к разделяемой памяти из этого экземпляра. Для корректной очистки ресурсов все экземпляры должны вызвать
close(), когда экземпляр больше не нужен. Обратите внимание: вызовclose()не приводит к уничтожению самого блока разделяемой памяти.
-
Запрашивает уничтожение базового блока разделяемой памяти. Для корректной очистки ресурсов
unlink()следует вызвать один раз (и только один) во всех процессах, которым нужен этот блок разделяемой памяти. После запроса на уничтожение блок разделяемой памяти может быть уничтожен сразу, а может и не быть; это поведение зависит от платформы. Попытки доступа к данным внутри блока после вызоваunlink()могут привести к ошибкам доступа к памяти. Примечание: последний процесс, освобождающий блок разделяемой памяти, может вызватьunlink()иclose()в любом порядке.
-
Представление memoryview содержимого блока разделяемой памяти.
-
Доступ только для чтения к уникальному имени блока разделяемой памяти.
-
Доступ только для чтения к размеру блока разделяемой памяти в байтах.
В следующем примере показано низкоуровневое использование экземпляров 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: доступ к одному и тому же 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
-
Подкласс
multiprocessing.managers.BaseManager, который можно использовать для управления блоками разделяемой памяти между процессами.Вызов
start()для экземпляраSharedMemoryManagerприводит к запуску нового процесса. Единственная задача этого процесса — управлять жизненным циклом всех созданных через него блоков разделяемой памяти. Чтобы освободить все блоки разделяемой памяти, которыми управляет этот процесс, вызовите для экземпляраshutdown(). Это приводит к вызовуunlink()для всех объектовSharedMemory, которыми управляет этот процесс, после чего сам процесс останавливается. Создавая экземплярыSharedMemoryчерезSharedMemoryManager, мы избавляемся от необходимости вручную отслеживать освобождение ресурсов разделяемой памяти и инициировать его.Этот класс предоставляет методы для создания и возврата экземпляров
SharedMemory, а также для создания похожего на список объекта (ShareableList), основанного на разделяемой памяти.Описание унаследованных необязательных аргументов address и authkey, а также сведения о том, как их можно использовать для подключения к существующей службе
SharedMemoryManagerиз других процессов, приведены в разделеBaseManager.-
Создаёт и возвращает новый объект
SharedMemoryуказанного размера size в байтах.
-
Создаёт и возвращает новый объект
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.
-
Предоставляет изменяемый объект, похожий на список, в котором все значения хранятся в блоке разделяемой памяти. Это ограничивает набор допустимых для хранения значений следующими встроенными типами данных:
-
int(64-разрядное целое со знаком) floatbool-
str(менее 10 млн байт при кодировании в UTF-8) -
bytes(менее 10 млн байт) None
Также этот объект существенно отличается от встроенного типа
list: общую длину таких списков нельзя изменить (т. е. нельзя использоватьappend(),insert()и т. д.), а срезы не поддерживают динамическое создание новых экземпляровShareableList.sequence используется для заполнения нового
ShareableListзначениями. Чтобы вместо этого подключиться к уже существующемуShareableListпо его уникальному имени, заданному для разделяемой памяти, укажитеNone.name — уникальное имя запрашиваемой разделяемой памяти, как описано в определении
SharedMemory. При подключении к существующемуShareableListукажите уникальное имя его блока разделяемой памяти, оставив для sequence значениеNone.Примечание
Известна проблема со значениями
bytesиstr. Если они оканчиваются на\x00нулевых байтов или символов, при получении по индексу изShareableListони могут быть незаметно удалены. Это поведение.rstrip(b'\x00')считается ошибкой и может быть исправлено в будущем. См. gh-106939.Если удаление конечных нулей при rstrip мешает работе приложения, обойдите проблему: всегда добавляйте к таким значениям в конце дополнительный ненулевой байт при сохранении и всегда удаляйте его при получении:
>>> from multiprocessing import shared_memory >>> nul_bug_demo = shared_memory.ShareableList(['?\x00', b'\x03\x02\x01\x00\x00\x00']) >>> nul_bug_demo[0] '?' >>> nul_bug_demo[1] b'\x03\x02\x01' >>> nul_bug_demo.shm.unlink() >>> padded = shared_memory.ShareableList(['?\x00\x07', b'\x03\x02\x01\x00\x00\x00\x07']) >>> padded[0][:-1] '?\x00' >>> padded[1][:-1] b'\x03\x02\x01\x00\x00\x00' >>> padded.shm.unlink()
-
Возвращает количество вхождений value.
-
Возвращает индекс первого вхождения value. Если value отсутствует, вызывает
ValueError.
-
Атрибут только для чтения, содержащий формат упаковки
struct, используемый всеми хранящимися в данный момент значениями.
-
Экземпляр
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) можно сериализовать и десериализовать с помощью pickle. Обратите внимание: это всё равно будет тот же самый общий объект. Это происходит потому, что десериализованный объект имеет то же уникальное имя и просто подключается к существующему объекту с таким же именем (если объект всё ещё существует):
>>> 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()
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/multiprocessing.shared_memory.html