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

Kod źródłowy: Lib/multiprocessing/shared_memory.py

Dodane w wersji 3.8.


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

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

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

Создайте экземпляр класса SharedMemory для создания нового блока общей памяти или присоединения к существующему блоку общей памяти. Каждому блоку общей памяти присваивается уникальное имя. Таким образом, один процесс может создать блок общей памяти с определенным именем, а другой процесс может подключиться к этому же блоку общей памяти, используя то же имя.

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

Parametry:
  • name (str | None) – Уникальное имя запрошенной общей памяти в виде строки. Если при создании нового блока общей памяти для имени указано «Нет» (по умолчанию), будет сгенерировано новое имя.

  • create (bool) – Контролируйте, создается ли новый блок общей памяти («True») или присоединяется существующий блок общей памяти («False»).

  • size (int) – Запрошенное количество байтов при создании нового блока общей памяти. Поскольку некоторые платформы предпочитают выделять фрагменты памяти в зависимости от размера страницы памяти этой платформы, точный размер блока общей памяти может быть больше или равен запрошенному размеру. При присоединении к существующему блоку общей памяти параметр size игнорируется.

  • track (bool) – Если установлено значение «True», зарегистрируйте блок общей памяти в процессе отслеживания ресурсов на платформах, где ОС не делает этого автоматически. Трекер ресурсов обеспечивает правильную очистку общей памяти, даже если все остальные процессы, имеющие доступ к памяти, завершаются без этого. Процессы Python, созданные от общего предка с использованием средств multiprocessing, совместно используют один процесс отслеживания ресурсов, и время жизни сегментов общей памяти обрабатывается автоматически между этими процессами. Процессы Python, созданные любым другим способом, получат собственный трекер ресурсов при доступе к общей памяти с включенным track. Это приведет к удалению общей памяти трекером ресурсов первого завершившегося процесса. Чтобы избежать этой проблемы, пользователи subprocess или автономных процессов Python должны установить для track значение False, когда уже существует другой процесс, который выполняет учет. track игнорируется в Windows, которая имеет собственное отслеживание и автоматически удаляет общую память, когда все ее дескрипторы закрыты.

Zmienione w wersji 3.13: Добавлен параметр track.

close()

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

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

Этот метод не влияет на Windows, где единственный способ удалить блок общей памяти — закрыть все дескрипторы.

buf

Перегляд вмісту спільного блоку пам’яті.

name

Read-only доступ до унікального імені спільного блоку пам’яті.

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), який підтримується спільною пам’яттю.

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

SharedMemory(size)

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

ShareableList(sequence)

Создайте и верните новый объект ShareableList, инициализированный значениями из входной последовательности.

Следующий пример демонстрирует основные механизмы 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 - z ang. - Żaden

Он также заметно отличается от встроенного типа list тем, что эти списки не могут изменять свою общую длину (т.е. нет append(), insert() и т. д.) и не поддерживать динамическое создание новых экземпляров ShareableList посредством нарезки.

sequence используется для заполнения нового ShareableList, полного значений. Установите значение None, чтобы вместо этого присоединить его к уже существующему ShareableList по его уникальному имени общей памяти.

name — это уникальное имя запрошенной общей памяти, как описано в определении SharedMemory. При присоединении к существующему ShareableList укажите уникальное имя его блока общей памяти, оставив для sequence значение None.

Informacja

Известная проблема существует для значений bytes и str. Если они заканчиваются нулевыми байтами или символами \x00, они могут быть тихо удалены при извлечении их по индексу из ShareableList. Такое поведение .rstrip(b'\x00') считается ошибкой и может исчезнуть в будущем. См. gh-106939.

Для приложений, где удаление конечных нулей является проблемой, обойдите ее, всегда безоговорочно добавляя дополнительный байт, отличный от 0, в конец таких значений при сохранении и безоговорочно удаляя его при выборке:

>>> 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)

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

index(value)

Вернуть первую позицию индекса значения. Поднимите ValueError, если value отсутствует.

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

>>> 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()