multiprocessing.shared_memory — Общая память для прямого доступа между процессами
Исходный код: Lib/multiprocessing/shared_memory.py
Новое в версии 3.8.
Этот модуль предоставляет класс SharedMemory для выделения и управления общей памятью, к которой могут обращаться один или несколько процессов на многоядерной или симметричной многопроцессорной (SMP) машине. Для помощи в управлении жизненным циклом общей памяти, особенно между разными процессами, также предоставляется подкласс BaseManager, SharedMemoryManager, в модуле multiprocessing.managers.
В данном модуле общая память относится к блокам общей памяти «стиля System V» (хотя не обязательно реализуется именно так) и не относится к «распределённой общей памяти». Этот стиль общей памяти позволяет различным процессам потенциально читать и записывать в общую область динамической памяти. Обычно процессы ограничены доступом только к своему собственному пространству памяти процесса, но общая память позволяет обмениваться данными между процессами, избегая необходимости отправки сообщений между процессами, содержащими эти данные. Прямой обмен данными через память может обеспечить существенные преимущества производительности по сравнению с обменом данными через диск, сокет или другие средства связи, требующие сериализации/десериализации и копирования данных.
-
Создаёт новый блок общей памяти или подключается к существующему блоку общей памяти. Каждый блок общей памяти получает уникальное имя. Таким образом, один процесс может создать блок общей памяти с определённым именем, а другой процесс может подключиться к этому же блоку общей памяти, используя это же имя.
В качестве ресурса для обмена данными между процессами, блоки общей памяти могут существовать дольше, чем исходный процесс, который их создал. Когда процесс больше не нуждается в доступе к блоку общей памяти, который может по-прежнему потребоваться другим процессам, должен быть вызван метод
close(). Когда блок общей памяти больше не нужен ни одному процессу, следует вызвать методunlink()для обеспечения надлежащей очистки.name — уникальное имя запрашиваемой общей памяти, заданное как строка. При создании нового блока общей памяти, если для имени указано
None(по умолчанию), будет сгенерировано новое имя.create определяет, создаётся ли новый блок общей памяти (
True) или подключается существующий блок общей памяти (False).size задаёт запрашиваемое количество байтов при создании нового блока общей памяти. Поскольку некоторые платформы выбирают выделять блоки памяти, основанные на размере страницы памяти данной платформы, точный размер блока общей памяти может быть больше или равен запрошенному размеру. При подключении к существующему блоку общей памяти параметр
sizeигнорируется.-
Закрывает доступ к общей памяти из этого экземпляра. Для обеспечения надлежащей очистки ресурсов все экземпляры должны вызвать
close()после того, как экземпляр больше не нужен. Обратите внимание, что вызовclose()не приводит к уничтожению самого блока общей памяти.
-
Запрашивает уничтожение основного блока общей памяти. Для обеспечения надлежащей очистки ресурсов
unlink()должен быть вызван один раз (и только один раз) всеми процессами, которым необходим блок общей памяти. После запроса уничтожения блок общей памяти может быть или не быть немедленно уничтожен, и это поведение может отличаться на разных платформах. Попытки доступа к данным внутри блока общей памяти после того, какunlink()был вызван, могут привести к ошибкам доступа к памяти. Примечание: последний процесс, освобождающий свой захват на блоке общей памяти, может вызватьunlink()иclose()в любом порядке.
-
Представление памяти содержимого блока общей памяти.
-
Только для чтения уникальное имя блока общей памяти.
-
Только для чтения размер блока общей памяти в байтах.
-
Следующий пример демонстрирует низкоуровневое использование экземпляров 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
-
Подкласс
BaseManager, который можно использовать для управления блоками общей памяти между процессами.Вызов
start()на экземпляреSharedMemoryManagerзапускает новый процесс. Цель этого нового процесса — управление жизненным циклом всех блоков общей памяти, созданных через него. Для запуска освобождения всех управляемых этим процессом блоков общей памяти вызовитеshutdown()на экземпляре. Это вызывает вызовSharedMemory.unlink()для всех объектовSharedMemory, управляемых этим процессом, а затем останавливает сам процесс. Создавая экземплярыSharedMemoryчерезSharedMemoryManager, мы избегаем необходимости вручную отслеживать и вызывать освобождение ресурсов общей памяти.Этот класс предоставляет методы для создания и возврата экземпляров
SharedMemoryи для создания похожей на список структуры (ShareableList) на основе общей памяти.Обратитесь к
multiprocessing.managers.BaseManagerза описанием необязательных входных аргументов address и authkey и о том, как их можно использовать для подключения к существующейSharedMemoryManagerслужбе из других процессов.-
Создаёт и возвращает новый объект
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-битное),float,bool,str(меньше 10 МБ каждый при кодировании в utf-8),bytes(меньше 10 МБ каждый) иNoneвстроенными типами данных. Он также заметно отличается от встроенного типаlistтем, что эти списки не могут изменять свою общую длину (то есть, нет методов append, insert и т.п.) и не поддерживают динамическое создание новых экземпляровShareableListс помощью срезов.sequence используется для заполнения нового
ShareableListзначениями. Установлено в значениеNone, чтобы вместо этого подключиться к уже существующемуShareableListпо его уникальному имени общей памяти.name — уникальное имя запрашиваемой общей памяти, как описано в определении для
SharedMemory. При подключении к существующемуShareableList, укажите уникальное имя блока общей памяти, оставивsequenceустановленным в значениеNone.Примечание
Существует известная проблема с
bytesиstrзначениями. Если они заканчиваются\x00нулевыми байтами или символами, они могут быть неявным образом удалены при извлечении по индексу изShareableList. Это.rstrip(b'\x00')поведение считается ошибкой и может быть исправлено в будущем. См. gh-106939.Для приложений, где удаление следов нулей является проблемой, обойдите её, всегда добавляя дополнительный ненулевой байт в конец таких значений при сохранении и удаляя его при извлечении:
>>> 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. ВызываетValueError, еслиvalueотсутствует.
-
Только для чтения атрибут, содержащий формат упаковки
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) могут быть сериализованы и десериализованы при необходимости. Обратите внимание, что это по-прежнему будет тот же общий объект. Это происходит потому, что десериализованный объект имеет то же самое уникальное имя и просто подключается к существующему объекту с тем же именем (если объект всё ещё жив):
>>> 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–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/multiprocessing.shared_memory.html