Spec-Zone.ru › Python 3.13

multiprocessing.shared_memory — Разделяемая память для прямого доступа между процессами

Исходный код: Lib/multiprocessing/shared_memory.py

Добавлено в версии 3.8.

Этот модуль предоставляет класс SharedMemory для выделения и управления разделяемой памятью, доступной одному или нескольким процессам на многоядерной машине или машине с симметричной многопроцессорной архитектурой (SMP). Для управления жизненным циклом разделяемой памяти, особенно между отдельными процессами, в модуле multiprocessing.managers также предоставлен подкласс BaseManager — SharedMemoryManager.

В этом модуле под разделяемой памятью понимаются блоки разделяемой памяти «в стиле POSIX» (хотя их реализация не обязательно явно соответствует этому стилю); это понятие не относится к «распределённой разделяемой памяти». Такой тип разделяемой памяти позволяет отдельным процессам потенциально читать и записывать данные в общей области энергозависимой памяти. Обычно процессы имеют доступ только к собственной области памяти, но разделяемая память позволяет обмениваться данными между процессами и избавляет от необходимости передавать эти данные в сообщениях между процессами. Прямой обмен данными через память может обеспечить значительный прирост производительности по сравнению с обменом данными через диск, сокет или другие средства связи, требующие сериализации, десериализации и копирования данных.

class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0, *, track=True)

Создаёт экземпляр класса 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 игнорируется: эта система использует собственный механизм отслеживания и автоматически удаляет разделяемую память после закрытия всех дескрипторов, связанных с ней.

Изменено в версии 3.13: Добавлен параметр track.

close()

Закрывает файловый дескриптор/дескриптор разделяемой памяти для этого экземпляра. Метод close() следует вызвать, когда доступ к блоку разделяемой памяти из этого экземпляра больше не нужен. В зависимости от операционной системы базовая область памяти может быть освобождена или не освобождена, даже если все дескрипторы, связанные с ней, закрыты. Для надлежащей очистки используйте метод unlink().

unlink()

Удаляет базовый блок разделяемой памяти. Этот метод следует вызывать только один раз для каждого блока разделяемой памяти независимо от количества дескрипторов, связанных с ним, в том числе в других процессах. Методы unlink() и close() можно вызывать в любом порядке, однако попытка обратиться к данным в блоке разделяемой памяти после вызова unlink() может привести к ошибкам доступа к памяти в зависимости от платформы.

В Windows этот метод не действует: единственный способ удалить блок разделяемой памяти — закрыть все дескрипторы.

buf

Представление памяти (memoryview) содержимого блока разделяемой памяти.

name

Доступ только для чтения к уникальному имени блока разделяемой памяти.

size

Доступ только для чтения к размеру блока разделяемой памяти в байтах.

В следующем примере показано низкоуровневое использование экземпляров 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
class multiprocessing.managers.SharedMemoryManager([address[, authkey]])

Подкласс multiprocessing.managers.BaseManager, предназначенный для управления блоками разделяемой памяти между процессами.

Вызов start() для экземпляра SharedMemoryManager приводит к запуску нового процесса. Единственная задача этого процесса — управлять жизненным циклом всех созданных с его помощью блоков разделяемой памяти. Чтобы освободить все блоки разделяемой памяти, которыми управляет этот процесс, вызовите для экземпляра shutdown(). Это приводит к вызову unlink() для всех объектов SharedMemory, которыми управляет этот процесс, после чего сам процесс останавливается. Создавая экземпляры SharedMemory с помощью SharedMemoryManager, можно обойтись без ручного отслеживания ресурсов разделяемой памяти и запуска их освобождения.

Этот класс предоставляет методы для создания и возврата экземпляров SharedMemory, а также для создания объекта, подобного списку (ShareableList), данные которого хранятся в разделяемой памяти.

Описание унаследованных необязательных аргументов address и authkey, а также способов их использования для подключения к существующей службе SharedMemoryManager из других процессов см. в разделе BaseManager.

SharedMemory(size)

Создаёт и возвращает новый объект SharedMemory указанного размера size в байтах.

ShareableList(sequence)

Создаёт и возвращает новый объект 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.

class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)

Предоставляет изменяемый объект, подобный списку, все значения которого хранятся в блоке разделяемой памяти. Это ограничивает набор сохраняемых значений следующими встроенными типами данных:

  • 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()
count(value)

Возвращает количество вхождений value.

index(value)

Возвращает индекс первого вхождения value. Если value отсутствует, вызывает исключение ValueError.

format

Атрибут только для чтения, содержащий формат упаковки struct, используемый для всех хранящихся в данный момент значений.

shm

Экземпляр 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API