multiprocessing.shared_memory
— Общая память для прямого доступа между процессами¶
Исходный код: Lib/multiprocessing/shared_memory.py.
Добавлено в версии 3.8.
Этот модуль предоставляет класс SharedMemory
для выделения и управления общей памятью, к которой обращаются один или несколько процессов на многоядерной или симметричной многопроцессорной машине (SMP). Для помощи в управлении жизненным циклом общей памяти, особенно для разных процессов, подкласс BaseManager
, SharedMemoryManager
, также предоставляется в модуле multiprocessing.managers
.
В этом модуле общая память относится к блокам общей памяти в стиле «System V» (хотя не обязательно реализуется явно как таковая) и не относится к «распределенной общей памяти». Этот стиль разделяемой памяти позволяет различным процессам потенциально читать и записывать в общую (или разделяемую) область энергозависимой памяти. Процессы традиционно ограничены доступом только к своему собственному пространству памяти процесса, но общая память позволяет обмениваться данными между процессами, избегая необходимости посылать сообщения между процессами, содержащие эти данные. Обмен данными непосредственно через память может обеспечить значительные преимущества в производительности по сравнению с обменом данными через диск, сокет или другие коммуникации, требующие сериализации/десериализации и копирования данных.
-
class
multiprocessing.shared_memory.
SharedMemory
(name=None, create=False, size=0)¶ Создает новый блок общей памяти или присоединяется к существующему блоку общей памяти. Каждому блоку общей памяти присваивается уникальное имя. Таким образом, один процесс может создать блок общей памяти с определенным именем, а другой процесс может присоединиться к этому же блоку общей памяти с тем же именем.
Как ресурс для обмена данными между процессами, блоки общей памяти могут пережить первоначальный процесс, который их создал. Когда одному процессу больше не нужен доступ к блоку разделяемой памяти, который может еще понадобиться другим процессам, следует вызвать метод
close()
. Когда блок общей памяти больше не нужен ни одному процессу, следует вызвать методunlink()
, чтобы обеспечить надлежащую очистку.name - уникальное имя для запрашиваемой общей памяти, заданное в виде строки. При создании нового блока разделяемой памяти, если для имени указано
None
(по умолчанию), будет сгенерировано новое имя.create управляет тем, создается ли новый блок общей памяти (
True
) или присоединяется существующий блок общей памяти (False
).size указывает запрашиваемое количество байт при создании нового блока общей памяти. Поскольку некоторые платформы предпочитают выделять фрагменты памяти на основе размера страницы памяти этой платформы, точный размер блока разделяемой памяти может быть больше или равен запрашиваемому размеру. При присоединении к существующему блоку разделяемой памяти параметр
size
игнорируется.-
close
()¶ Закрывает доступ к общей памяти данного экземпляра. Чтобы обеспечить надлежащую очистку ресурсов, все экземпляры должны вызывать
close()
, когда экземпляр больше не нужен. Обратите внимание, что вызовclose()
не приводит к уничтожению самого блока разделяемой памяти.
-
unlink
()¶ Запрашивает уничтожение базового блока разделяемой памяти. Чтобы обеспечить надлежащую очистку ресурсов,
unlink()
должен быть вызван один раз (и только один) во всех процессах, которым нужен блок общей памяти. После запроса на уничтожение блок общей памяти может быть немедленно уничтожен, а может и не быть, и это поведение может отличаться на разных платформах. Попытки получить доступ к данным внутри блока разделяемой памяти после вызоваunlink()
могут привести к ошибкам доступа к памяти. Примечание: последний процесс, освобождающий блок разделяемой памяти, может вызватьunlink()
иclose()
в любом порядке.
-
buf
¶ Просмотр содержимого блока общей памяти.
-
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 arrays, доступ к одному и тому же 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]])¶ Подкласс
BaseManager
, который может быть использован для управления общими блоками памяти между процессами.Вызов
start()
на экземпляреSharedMemoryManager
вызывает запуск нового процесса. Единственной целью этого нового процесса является управление жизненным циклом всех блоков общей памяти, созданных через него. Чтобы вызвать освобождение всех блоков общей памяти, управляемых этим процессом, вызовитеshutdown()
на экземпляре. Это вызовет вызовSharedMemory.unlink()
на всех объектахSharedMemory
, управляемых этим процессом, а затем остановит сам процесс. Создавая экземплярыSharedMemory
с помощьюSharedMemoryManager
, мы избегаем необходимости вручную отслеживать и инициировать освобождение ресурсов общей памяти.Этот класс предоставляет методы для создания и возврата экземпляров
SharedMemory
и для создания спископодобного объекта (ShareableList
), поддерживаемого общей памятью.См. раздел
multiprocessing.managers.BaseManager
для описания унаследованных дополнительных входных аргументов address и authkey и того, как их можно использовать для подключения к существующей службеSharedMemoryManager
из других процессов.-
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
,float
,bool
,str
(менее 10M байт каждый),bytes
(менее 10M байт каждый) иNone
. Он также заметно отличается от встроенного типаlist
тем, что эти списки не могут изменять свою общую длину (т.е. нельзя добавлять, вставлять и т.д.) и не поддерживают динамическое создание новых экземпляровShareableList
посредством нарезки.Последовательность используется при заполнении нового
ShareableList
значениями. Установите значениеNone
, чтобы вместо этого присоединиться к уже существующемуShareableList
по его уникальному имени в общей памяти.name - это уникальное имя для запрашиваемой разделяемой памяти, как описано в определении для
SharedMemory
. При присоединении к существующемуShareableList
укажите уникальное имя его блока разделяемой памяти, оставивsequence
установленным вNone
.-
count
(value)¶ Возвращает количество вхождений
value
.
-
index
(value)¶ Возвращает первую индексную позицию
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()
Python 3.10
Содержание
Дополнительно
Вы здесь:
-
Документация Django Python 3.10
- Стандартная библиотека Python
- Одновременное выполнение
multiprocessing.shared_memory
— Общая память для прямого доступа между процессами
- Одновременное выполнение
- Стандартная библиотека Python