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 игнорируется.
-
track (bool) – Если значение равно
True, зарегистрировать блок разделяемой памяти в процессе отслеживания ресурсов на платформах, где ОС не выполняет это автоматически. Отслеживатель ресурсов обеспечивает надлежащую очистку разделяемой памяти, даже если все остальные процессы с доступом к ней завершатся, не выполнив такую очистку. Процессы Python, созданные из общего родительского процесса с помощью средствmultiprocessing, используют один процесс отслеживания ресурсов; время жизни сегментов разделяемой памяти между этими процессами управляется автоматически. Процессы Python, созданные любым другим способом, при доступе к разделяемой памяти с включённым параметром track получают собственный процесс отслеживания ресурсов. В результате отслеживатель ресурсов первого завершившегося процесса удалит разделяемую память. Чтобы избежать этой проблемы, пользователямsubprocessили отдельных процессов Python следует присвоить track значениеFalse, если ведением учёта уже занимается другой процесс. В Windows параметр track игнорируется: эта система использует собственный механизм отслеживания и автоматически удаляет разделяемую память после закрытия всех дескрипторов, связанных с ней.
-
name (str | None) – Уникальное имя запрашиваемой разделяемой памяти, заданное в виде строки. При создании нового блока разделяемой памяти, если в качестве имени указано
Изменено в версии 3.13: Добавлен параметр track.
-
Закрывает файловый дескриптор/дескриптор разделяемой памяти для этого экземпляра. Метод
close()следует вызвать, когда доступ к блоку разделяемой памяти из этого экземпляра больше не нужен. В зависимости от операционной системы базовая область памяти может быть освобождена или не освобождена, даже если все дескрипторы, связанные с ней, закрыты. Для надлежащей очистки используйте методunlink().
-
Удаляет базовый блок разделяемой памяти. Этот метод следует вызывать только один раз для каждого блока разделяемой памяти независимо от количества дескрипторов, связанных с ним, в том числе в других процессах. Методы
unlink()иclose()можно вызывать в любом порядке, однако попытка обратиться к данным в блоке разделяемой памяти после вызоваunlink()может привести к ошибкам доступа к памяти в зависимости от платформы.В Windows этот метод не действует: единственный способ удалить блок разделяемой памяти — закрыть все дескрипторы.
-
Представление памяти (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значениями. Укажите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–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/multiprocessing.shared_memory.html