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()в любом порядке.
-
Представление памяти содержимого блока общей памяти.
-
Доступ только для чтения к уникальному имени блока общей памяти.
-
Доступ только для чтения к размеру блока общей памяти в байтах.
В следующем примере показано низкоуровневое использование экземпляров 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значениями. Укажите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. Если 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.11/library/multiprocessing.shared_memory.html