multiprocessing.shared_memory
— Delat minne för direktåtkomst mellan processer¶
Källkod: Lib/multiprocessing/shared_memory.py
Tillagd i version 3.8.
Den här modulen innehåller en klass, SharedMemory
, för allokering och hantering av delat minne som kan användas av en eller flera processer på en flerkärnig eller symmetrisk multiprocessormaskin (SMP). För att underlätta livscykelhanteringen av delat minne, särskilt mellan olika processer, finns också en underklass till BaseManager
, SharedMemoryManager
, i modulen multiprocessing.managers
.
I den här modulen avser delat minne delade minnesblock i POSIX-stil (även om de inte nödvändigtvis implementeras explicit som sådana) och inte ”distribuerat delat minne”. Denna typ av delat minne gör det möjligt för olika processer att potentiellt läsa och skriva till en gemensam (eller delad) region i det flyktiga minnet. Processer är normalt begränsade till att bara ha tillgång till sitt eget processminnesutrymme, men med delat minne kan data delas mellan processer, vilket gör att man inte behöver skicka meddelanden mellan processerna som innehåller dessa data. Att dela data direkt via minnet kan ge betydande prestandafördelar jämfört med att dela data via disk eller socket eller annan kommunikation som kräver serialisering/deserialisering och kopiering av data.
- class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0, *, track=True)¶
Skapa en instans av
SharedMemory
-klassen för att antingen skapa ett nytt delat minnesblock eller koppla till ett befintligt delat minnesblock. Varje delat minnesblock tilldelas ett unikt namn. På så sätt kan en process skapa ett shared memory-block med ett visst namn och en annan process kan koppla till samma shared memory-block med samma namn.Som en resurs för att dela data mellan processer kan delade minnesblock överleva den ursprungliga processen som skapade dem. När en process inte längre behöver tillgång till ett delat minnesblock som fortfarande kan behövas av andra processer, bör metoden
close()
anropas. När ett delat minnesblock inte längre behövs av någon process bör metodenunlink()
anropas för att säkerställa korrekt upprensning.- Parametrar:
name (str | None) – Det unika namnet för det begärda delade minnet, angivet som en sträng. Om
None
(standard) anges för namnet när ett nytt block med delat minne skapas, kommer ett nytt namn att genereras.create (bool) – Styr om ett nytt delat minnesblock ska skapas (
True
) eller om ett befintligt delat minnesblock ska kopplas till (False
).size (int) – Det begärda antalet byte när ett nytt delat minnesblock skapas. Eftersom vissa plattformar väljer att allokera minnesbitar baserat på plattformens minnessidstorlek, kan den exakta storleken på det delade minnesblocket vara större eller lika med den begärda storleken. När du ansluter till ett befintligt delat minnesblock ignoreras parametern size.
track (bool) – När
True
, registrera det delade minnesblocket med en resursspårningsprocess på plattformar där operativsystemet inte gör detta automatiskt. Resursspåraren säkerställer korrekt rensning av det delade minnet även om alla andra processer med tillgång till minnet avslutas utan att göra det. Python-processer som skapats från en gemensam förfader med hjälp avmultiprocessing
delar en enda resursspårningsprocess, och livstiden för delade minnessegment hanteras automatiskt mellan dessa processer. Python-processer som skapas på något annat sätt kommer att få sin egen resursspårare när de använder delat minne med track aktiverat. Detta kommer att leda till att det delade minnet raderas av resursspåraren för den första process som avslutas. För att undvika detta problem bör användare avsubprocess
eller fristående Python-processer sätta track tillFalse
när det redan finns en annan process på plats som sköter bokföringen. track ignoreras i Windows, som har sin egen spårning och automatiskt raderar delat minne när alla handtag till det har stängts.
Ändrad i version 3.13: Parametern track har lagts till.
- close()¶
Stäng filbeskrivaren/handtaget till det delade minnet från den här instansen.
close()
bör anropas när åtkomst till det delade minnesblocket från den här instansen inte längre behövs. Beroende på operativsystem kan det hända att det underliggande minnet inte frigörs även om alla handtag till det har stängts. För att säkerställa korrekt upprensning, använd metodenunlink()
.
- unlink()¶
Ta bort det underliggande delade minnesblocket. Detta bör bara anropas en gång per delat minnesblock oavsett antalet handtag till det, även i andra processer.
unlink()
ochclose()
kan anropas i valfri ordning, men om du försöker komma åt data i ett delat minnesblock efterunlink()
kan det leda till minnesåtkomstfel, beroende på plattform.Den här metoden har ingen effekt i Windows, där det enda sättet att ta bort ett delat minnesblock är att stänga alla handtag.
- buf¶
En minnesvy av innehållet i det delade minnesblocket.
- name¶
Skrivskyddad åtkomst till det unika namnet på det delade minnesblocket.
- size¶
Skrivskyddad åtkomst till storleken i bytes på det delade minnesblocket.
Följande exempel demonstrerar lågnivåanvändning av SharedMemory
-instanser:
>>> 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
Följande exempel visar en praktisk användning av klassen SharedMemory
med NumPy matriser, med åtkomst till samma numpy.ndarray
från två olika Python-skal:
>>> # 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]])¶
En underklass till
multiprocessing.managers.BaseManager
som kan användas för hantering av delade minnesblock mellan processer.Ett anrop till
start()
på en instans avSharedMemoryManager
gör att en ny process startas. Den nya processens enda syfte är att hantera livscykeln för alla delade minnesblock som skapas genom den. För att utlösa frisläppandet av alla delade minnesblock som hanteras av den processen, anropashutdown()
på instansen. Detta utlöser ettunlink()
-anrop på allaSharedMemory
-objekt som hanteras av den processen och stoppar sedan själva processen. Genom att skapaSharedMemory
-instanser genom enSharedMemoryManager
slipper vi manuellt spåra och utlösa frigörandet av delade minnesresurser.Den här klassen innehåller metoder för att skapa och returnera
SharedMemory
-instanser och för att skapa ett listliknande objekt (ShareableList
) som stöds av delat minne.Se
BaseManager
för en beskrivning av de ärvda valfria inmatningsargumenten address och authkey och hur de kan användas för att ansluta till en befintlig tjänstSharedMemoryManager
från andra processer.- SharedMemory(size)¶
Skapar och returnerar ett nytt
SharedMemory
-objekt med den angivna storleken i byte.
- ShareableList(sequence)¶
Skapar och returnerar ett nytt
ShareableList
-objekt, initierat med värdena från indatans sekvens.
Följande exempel visar de grundläggande mekanismerna i en 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
I följande exempel visas ett potentiellt mer praktiskt mönster för användning av SharedMemoryManager
-objekt via with
-satsen för att säkerställa att alla delade minnesblock frigörs när de inte längre behövs:
>>> 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
När du använder en SharedMemoryManager
i en with
-sats, frigörs alla delade minnesblock som skapats med den hanteraren när with
-satsens kodblock är färdigt exekverat.
- class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)¶
Tillhandahåller ett föränderligt listliknande objekt där alla värden som lagras i objektet lagras i ett delat minnesblock. Detta begränsar lagringsbara värden till följande inbyggda datatyper:
int
(signerad 64-bitars)str
(mindre än 10M byte vardera när de kodas som UTF-8)bytes
(mindre än 10M bytes vardera)Ingen
Den skiljer sig också markant från den inbyggda
list
-typen genom att dessa listor inte kan ändra sin totala längd (dvs. ingenappend()
,insert()
, etc.) och inte stöder det dynamiska skapandet av nyaShareableList
-instanser via slicing.sequence används för att fylla en ny
ShareableList
full med värden. Sätt tillNone
för att istället koppla till en redan existerandeShareableList
med dess unika namn i det delade minnet.name är det unika namnet för det begärda delade minnet, enligt beskrivningen i definitionen för
SharedMemory
. När du ansluter till en befintligShareableList
, ange det unika namnet på det delade minnesblocket medan sequence är satt tillNone
.Anteckning
Ett känt problem finns för
bytes
ochstr
-värden. Om de slutar med\x00
nollbytes eller tecken, kan dessa tyst avlägsnas när de hämtas med index frånShareableList
. Detta.rstrip(b'\x00')
-beteende anses vara en bugg och kan försvinna i framtiden. Se gh-106939.För applikationer där rstripping av efterföljande nollor är ett problem, kan du kringgå det genom att alltid ovillkorligen lägga till en extra icke-0-byte i slutet av sådana värden vid lagring och ovillkorligen ta bort den vid hämtning:
>>> 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)¶
Returnera antalet förekomster av value.
- index(value)¶
Returnerar första indexpositionen för value. Utlöser
ValueError
om value inte finns.
- format¶
Skrivskyddat attribut som innehåller förpackningsformatet
struct
som används av alla aktuella lagrade värden.
- shm¶
Den
SharedMemory
-instans där värdena lagras.
Följande exempel visar grundläggande användning av en ShareableList
-instans:
>>> 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
Följande exempel visar hur en, två eller många processer kan komma åt samma ShareableList
genom att ange namnet på det bakomliggande delade minnesblocket:
>>> 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()
Följande exempel visar att ShareableList
(och underliggande SharedMemory
) objekt kan picklas och unpicklas vid behov. Notera, att det fortfarande kommer att vara samma delade objekt. Detta händer eftersom det deserialiserade objektet har samma unika namn och bara är kopplat till ett befintligt objekt med samma namn (om objektet fortfarande är vid liv):
>>> 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()