multiprocessing.shared_memory — Общая память для прямого доступа между процессами
Исходный код: Lib/multiprocessing/shared_memory.py
Добавлена в версии 3.8.
Этот модуль предоставляет класс SharedMemory для выделения и управления общей памятью, к которой могут получить доступ один или несколько процессов на многоядерной или симметричной многопроцессорной (SMP) машине. Для помощи в управлении жизненным циклом общей памяти, особенно между различными процессами, также предоставляется подкласс BaseManager, SharedMemoryManager, в модуле multiprocessing.managers.
В этом модуле общая память относится к блокам общей памяти в стиле 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), основанного на общей памяти.Обратитесь к
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-битное со знаком) 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. Вызовет
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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/multiprocessing.shared_memory.html