multiprocessing.shared_memory
— Shared memory for direct access across processes¶
Código fuente: Lib/multiprocessing/shared_memory.py
Nuevo en la versión 3.8.
Este módulo proporciona una clase, SharedMemory
, para la asignación y administración de memoria compartida entre uno o más procesos en una máquina con varios núcleos o varios procesadores simétrico (SMP). Para facilitar la gestión del ciclo de vida de la memoria compartida, especialmente entre múltiples procesos, el módulo multiprocessing.managers
también proporciona la clase SharedMemoryManager
, una subclase de BaseManager
.
En este módulo, la memoria compartida se refiere a bloques de memoria compartida de «Sistema estilo V» (aunque no necesariamente se implementa explícitamente como tal) y no se refiere a «memoria compartida distribuida». Este tipo de memoria compartida permite que múltiples procesos lean y escriban en un área común (o compartida) de memoria volátil. Normalmente, los procesos solo tienen acceso a su propio espacio de memoria; la memoria compartida permite compartir datos entre procesos, lo que evita que tengan que enviar estos datos por mensaje. Compartir datos directamente a través de la memoria puede proporcionar importantes beneficios de rendimiento en comparación con compartir datos a través de un disco o socket u otras comunicaciones que requieren la serialización/deserialización y copia de datos.
-
class
multiprocessing.shared_memory.
SharedMemory
(name=None, create=False, size=0)¶ Crea un nuevo bloque de memoria compartida o guarda un bloque ya existente. Se debe dar un nombre único a cada bloque de memoria compartida; por lo tanto, un proceso puede crear un nuevo bloque de memoria compartida con un nombre particular y un proceso diferente se puede conectar a ese mismo bloque de memoria compartida usando ese mismo nombre.
Como un recurso para compartir datos entre procesos, los bloques de memoria compartida pueden sobrevivir al proceso original que los creó. Cuando un proceso ya no necesita acceso a un bloque de memoria compartida que otros procesos aún podrían necesitar, se debe llamar al método
close()
. Cuando un proceso ya no necesita un bloque de memoria compartida, se debe llamar al métodounlink()
para garantizar una limpieza adecuada.name es el nombre único para la memoria compartida solicitada, especificada como una cadena de caracteres. Al crear un nuevo bloque de memoria compartida, si se proporciona
None
(valor por defecto) para el nombre, se generará un nombre nuevo.create controla si se crea un nuevo bloque de memoria compartida (
True
) o si se adjunta un bloque de memoria compartida existente (False
).size especifica el número solicitado de bytes al crear un nuevo bloque de memoria compartida. Debido a que algunas plataformas eligen asignar fragmentos de memoria en función del tamaño de página de memoria de esa plataforma, el tamaño exacto del bloque de memoria compartida puede ser mayor o igual al tamaño solicitado. Cuando se conecta a un bloque de memoria compartida existente, se ignora el parámetro
size
.-
close
()¶ Cierra el acceso a la memoria compartida desde esta instancia. Para garantizar la limpieza adecuada de los recursos, todas las instancias deben llamar a
close()
una vez que la instancia ya no sea necesaria. Tenga en cuenta que llamar aclose()
no causa que el bloque de memoria compartida se destruya.
-
unlink
()¶ Solicita que se destruya el bloque de memoria compartida subyacente. Para garantizar la limpieza adecuada de los recursos, se debe llamar a
unlink()
una vez (y solo una vez) en todos los procesos que necesitan el bloque de memoria compartida. Después de solicitar su destrucción, un bloque de memoria compartida puede o no destruirse de inmediato y este comportamiento puede diferir entre plataformas. Los intentos de acceder a los datos dentro del bloque de memoria compartida después de que se haya llamado aunlink()
pueden provocar errores de acceso a la memoria. Nota: el último proceso para liberar el bloque de memoria compartida puede llamar aunlink()
yclose()
en cualquier orden.
-
buf
¶ Un memoryview del contenido del bloque de memoria compartida.
-
name
¶ Acceso de solo lectura al nombre único del bloque de memoria compartida.
-
size
¶ Acceso de solo lectura al tamaño en bytes del bloque de memoria compartida.
-
El siguiente ejemplo muestra el uso de bajo nivel de instancias de 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
The following example demonstrates a practical use of the SharedMemory
class with NumPy arrays, accessing the
same numpy.ndarray
from two distinct Python shells:
>>> # 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]])¶ Una subclase de
BaseManager
que se puede utilizar para la gestión de bloques de memoria compartida en todos los procesos.Una llamada al método
start()
en una instancia deSharedMemoryManager
hace que se inicie un nuevo proceso. El único propósito de este nuevo proceso es administrar el ciclo de vida de todos los bloques de memoria compartida creados a través de él. Para activar la liberación de todos los bloques de memoria compartida administrados por ese proceso, llama al métodoshutdown()
en la instancia. Esto desencadena una llamada al métodoSharedMemory.unlink()
en todos los objetos de la claseSharedMemory
administrados por ese proceso y luego detiene el proceso en sí. Al crear instancias deSharedMemory
a través de unSharedMemoryManager
, evitamos la necesidad de rastrear manualmente y activar la liberación de recursos de memoria compartida.Esta clase proporciona métodos para crear y retornar instancias
SharedMemory
y para crear un objeto de tipo lista (ShareableList
) basados en memoria compartida.Consulte
multiprocessing.managers.BaseManager
para obtener una descripción de los argumentos heredados opcionales address y authkey y cómo se deben usar para registrar un servicioSharedMemoryManager
desde otro proceso.-
SharedMemory
(size)¶ Crea y retorna un nuevo objeto
SharedMemory
con el tamañosize
especificado en bytes.
-
ShareableList
(sequence)¶ Crea y retorna un nuevo objeto
ShareableList
, inicializado por los valores de la entradasequence
.
-
El siguiente ejemplo muestra los mecanismos básicos de 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
El siguiente ejemplo muestra un patrón más conveniente para usar un objeto SharedMemoryManager
con la sentencia with
para asegurarse de que todos los bloques de memoria se liberen cuando ya no son necesarios. Esto suele ser más práctico que el ejemplo anterior:
>>> 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
Cuando se utiliza un SharedMemoryManager
en una sentencia with
, los bloques de memoria compartida creados por ese administrador se liberan cuando la sentencias dentro del bloque de código with
finaliza la ejecución.
-
class
multiprocessing.shared_memory.
ShareableList
(sequence=None, *, name=None)¶ Construye un objeto mutable compatible con el tipo de lista cuyos valores se almacenan en un bloque de memoria compartida. Esto reduce los valores de tipo que se pueden almacenar solo a tipos de datos nativos
int
,float
,bool
,str
(menos de 10 MB cada uno),bytes
(menos de 10 MB cada uno) yNone
. Otra diferencia importante con una lista nativa es que es imposible cambiar el tamaño (es decir, sin adición al final de la lista, sin inserción, etc.) y que no es posible crear nuevas instancias deShareableList
mediante la división.sequence se utiliza para completar una nueva
ShareableList
con valores. Establezca enNone
para registrar en su lugar unaShareableList
ya existente por su nombre único de memoria compartida.name es el nombre único para la memoria compartida solicitada, como se describe en la definición de
SharedMemory
. Al adjuntar a unaShareableList
existente, especifique el nombre único de su bloque de memoria compartida mientras dejasequence
establecida enNone
.-
count
(value)¶ Retorna el número de ocurrencias de
value
.
-
index
(value)¶ Retorna la primera posición del índice de
value
. LanzaValueError
sivalue
no está presente.
-
format
¶ Atributo de solo lectura que contiene el formato de empaquetado
struct
utilizado por todos los valores almacenados actualmente.
-
shm
¶ La instancia de
SharedMemory
donde se almacenan los valores.
-
El siguiente ejemplo muestra el uso básico de una instancia 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
El siguiente ejemplo muestra cómo uno, dos o muchos procesos pueden acceder al mismo ShareableList
al proporcionar el nombre del bloque de memoria compartida detrás de él:
>>> 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()
El siguiente ejemplo demuestra que los objetos ShareableList
(y de forma implícita SharedMemory
) pueden ser serializados (pickled) y deserializados (unpickled) si es que se necesitan. Nota, Este va a seguir siendo el mismo objeto compartido. Esto sucede, porque el objeto deserializado tiene el mismo nombre único y simplemente se adjunta a un objeto existente con el mismo nombre (si el objeto todavía sigue vivo):
>>> 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()