multiprocessing.shared_memory
--- Shared memory for direct access across processes¶
Code source : Lib/multiprocessing/shared_memory.py
Nouveau dans la version 3.8.
Ce module fournit une classe, SharedMemory
, pour l'allocation et la gestion de mémoire partagée entre un ou plusieurs processus sur une machine à plusieurs cœurs ou à multiprocesseurs (architecture symmetric multiprocessor ou SMP). Pour faciliter la gestion du cycle de vie de la mémoire partagée, tout particulièrement entre plusieurs processus, le module multiprocessing.managers
fournit aussi la classe SharedMemoryManager
, sous-classe de BaseManager
.
Dans ce module, il faut entendre « mémoire partagée » au sens de « blocs de mémoire partagée à la mode System V » (même si l'implémentation peut différer), et non au sens de « mémoire distribuée ». Ce type de mémoire partagée permet à plusieurs processus d'écrire dans une zone commune (ou « partagée ») de la mémoire vive. Normalement, les processus n'ont accès qu'à leur propre espace mémoire ; la mémoire partagée permet justement le partage de données entre des processus, ce qui leur évite d'avoir à s'envoyer ces données par message. Échanger des données par mémoire partagée peut amener des gains de performance substantiels par rapport aux échanges via le disque dur, des connecteurs ou d'autres canaux qui nécessitent de sérialiser et de désérialiser les données.
-
class
multiprocessing.shared_memory.
SharedMemory
(name=None, create=False, size=0)¶ Crée un nouveau bloc de mémoire partagée ou enregistre un bloc déjà existant. Un nom unique doit être donné à chaque bloc de mémoire partagée ; ainsi, un processus peut créer un nouveau bloc de mémoire partagée avec un nom fixé et un autre processus peut enregistrer le même bloc, à partir de son nom.
Puisque qu'il permet de partager des données entre processus, un bloc de mémoire partagée peut survivre au processus qui l'a créé. Lorsqu'un processus n'a plus besoin d'un bloc — qui peut toujours être en cours d'utilisation par un autre — il doit appeler la méthode
close()
. Quand tous les processus ont fini d'utiliser ce bloc, il faut appeler la méthodeunlink()
pour le libérer.name est le nom (une chaîne de caractères) unique de la mémoire partagée à allouer. Lors de la création d'un nouveau bloc mémoire, si
None
(valeur par défaut) est passé comme nom, un nouveau nom est généré.create indique si un nouveau bloc doit être alloué (
True
) ou si on enregistre un bloc déjà existant (False
).size définit le nombre d'octets à allouer. Comme certaines plates-formes choisissent d'allouer les blocs mémoire en multiples de la taille de la page mémoire de la plate-forme, la taille réellement allouée peut être supérieure à la taille demandée. Lors de l'enregistrement d'un bloc déjà existant, le paramètre
size
est ignoré.-
close
()¶ Empêche les accès ultérieurs à la mémoire partagée depuis cette instance ; toutes les instances doivent appeler
close()
pour s'assurer que les ressources sont bien libérées. Notez qu'appelerclose()
ne libère pas la mémoire elle-même.
-
unlink
()¶ Initie la libération de la mémoire partagée sous-jacente. Pour être sûr que les ressources sont libérées correctement,
unlink()
doit être appelée une (et une seule) fois par tous les processus qui ont utilisé le bloc partagé. Après avoir initié la destruction d'un bloc mémoire, le bloc peut ne pas être détruit immédiatement ; ce comportement dépend de la plate-forme. Accéder aux données d'un bloc de mémoire partagée après l'appel àunlink()
peut provoquer une erreur mémoire. Notez que le dernier processus à libérer le bloc mémoire de mémoire partagée peut appelerunlink()
etclose()
dans n'importe quel ordre.
-
buf
¶ Une memoryview du contenu du bloc de mémoire partagée.
-
name
¶ Nom unique du bloc de mémoire partagée (lecture seule).
-
size
¶ Taille en octets du bloc de mémoire partagée (lecture seule).
-
L'exemple qui suit montre un exemple d'utilisation bas niveau d'instances 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]])¶ Une sous-classe de
BaseManager
pour gérer des blocs de mémoire partagée entre processus.Un appel à
start()
depuis une instanceSharedMemoryManager
lance un nouveau processus dont le seul but est de gérer le cycle de vie des blocs mémoires qu'il a créés. La méthodeshutdown()
de l'instance déclenche la libération de tous les blocs mémoires gérés par ce processus. Elle appelleSharedMemory.unlink()
sur tous les objetsSharedMemory
gérés par ce processus et l'arrête ensuite. Créer des instances deSharedMemory
par l'intermédiaire d'unSharedMemoryManager
évite d'avoir à gérer et à libérer manuellement les ressources mémoire partagées.Cette classe fournit des méthodes pour créer et renvoyer des instances de
SharedMemory
et pour créer des objets compatibles liste (ShareableList
) basés sur la mémoire partagée.Référez-vous à
multiprocessing.managers.BaseManager
pour la description des arguments optionnels hérités address et authkey, et comment ceux-ci doivent être utilisés pour enregistrer un service deSharedMemoryManager
depuis un autre processus.-
SharedMemory
(size)¶ Crée et renvoie un nouvel objet
SharedMemory
de taillesize
octets.
-
ShareableList
(sequence)¶ Crée et renvoie un nouvel objet
ShareableList
, initialisé à partir des valeurs de lasequence
en entrée.
-
L'exemple qui suit illustre les mécanismes de base 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
L'exemple suivant montre comment utiliser un objet SharedMemoryManager
avec l'instruction with
pour être sûr que tous les blocs mémoire sont libérés quand ils ne sont plus nécessaires. C'est souvent plus pratique que l'exemple précédent :
>>> 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
Lors de l'utilisation d'un SharedMemoryManager
dans une instruction with
, les blocs de mémoire partagée créés par ce gestionnaire sont tous libérés quand les instructions à l'intérieur du bloc with
ont été exécutées.
-
class
multiprocessing.shared_memory.
ShareableList
(sequence=None, *, name=None)¶ Construit un objet muable compatible avec le type liste dont toutes les valeurs sont stockées dans un bloc de mémoire partagée. Ceci limite le type des valeurs pouvant être stockées aux types natifs
int
,float
,bool
,str
(de moins de 10 Mo chacune),bytes
(de moins de 10 Mo chacun) etNone
. Une autre différence majeure avec unelist
native réside dans le fait qu'il est impossible de changer la taille (c.-à-d. pas d'ajout en fin de liste, ni d'insertion etc.) et qu'il n'est pas possible de créer de nouvelles instances deShareableList
par découpage.sequence sert à créer une nouvelle
ShareableList
avec des valeurs. Mettez-le àNone
pour enregistrer uneShareableList
déjà existante, en renseignant son nom unique.name est le nom unique de la mémoire partagée demandée, tel que décrit dans la définition de
SharedMemory
. Pour enregistrer uneShareableList
déjà existante, renseignez le nom unique du bloc de mémoire partagée et laissezsequence
àNone
.-
count
(value)¶ Renvoie le nombre d’occurrences de
value
.
-
index
(value)¶ Renvoie l'indice de la première occurrence de
value
. Lève uneValueError
sivalue
n'est pas présent.
-
format
¶ Attribut en lecture seule contenant le format d’agrégation
struct
utilisé par les valeurs déjà stockées.
-
shm
¶ Instance de
SharedMemory
dans laquelle les valeurs sont stockées.
-
L'exemple qui suit illustre un cas d'usage de base d'une instance de 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
L'exemple ci-dessous montre comment un, deux ou un grand nombre de processus peuvent accéder à une ShareableList
commune à partir du nom du bloc mémoire partagé sous-jacent :
>>> 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()
L'exemple ci-dessous montre comment une ShareableList
(et le SharedMemory
sous-jacent) peut être sérialisée et désérialisée. Gardez bien à l'esprit que c'est toujours le même objet, car l'objet désérialisé a le même nom unique et est tout simplement attaché à un objet déjà existant du même nom (si cet objet est toujours en vie).
>>> 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()