Spec-Zone.ru › Python 3.14

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

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

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

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

В этом модуле под разделяемой памятью подразумеваются блоки разделяемой памяти «в стиле 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 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/multiprocessing.shared_memory.html

Spec-Zone.ru

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