# `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étodo `unlink()` 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 a `close()` 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 a `unlink()` pueden provocar errores de acceso a la memoria. Nota: el último proceso para liberar el bloque de memoria compartida puede llamar a `unlink()` y `close()` 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 de `SharedMemoryManager` 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étodo `shutdown()` en la instancia. Esto desencadena una llamada al método `SharedMemory.unlink()` en todos los objetos de la clase `SharedMemory` administrados por ese proceso y luego detiene el proceso en sí. Al crear instancias de `SharedMemory` a través de un `SharedMemoryManager`, 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 servicio `SharedMemoryManager` desde otro proceso.

  `SharedMemory`(size)

    Crea y retorna un nuevo objeto `SharedMemory` con el tamaño `size` especificado en bytes.

  `ShareableList`(sequence)

    Crea y retorna un nuevo objeto `ShareableList`, inicializado por los valores de la entrada `sequence`.

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) y `None`. 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 de `ShareableList` mediante la división.

  sequence se utiliza para completar una nueva `ShareableList` con valores. Establezca en `None` para registrar en su lugar una `ShareableList` 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 una `ShareableList` existente, especifique el nombre único de su bloque de memoria compartida mientras deja `sequence` establecida en `None`.

  `count`(value)

    Retorna el número de ocurrencias de `value`.

  `index`(value)

    Retorna la primera posición del índice de `value`. Lanza `ValueError` si `value` 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()