multiprocessing.shared_memory
— 프로세스 간 직접 액세스를 위한 공유 메모리¶
소스 코드: Lib/multiprocessing/shared_memory.py
Added in version 3.8.
이 모듈은 멀티 코어나 대칭 멀티 프로세서 (SMP) 기계에서 하나 이상의 프로세스가 액세스할 공유 메모리의 할당과 관리를 위한 클래스 SharedMemory
를 제공합니다. 특히 별개의 프로세스에 걸친 공유 메모리의 수명 주기 관리를 지원하기 위해, BaseManager
서브 클래스인 SharedMemoryManager
도 multiprocessing.managers
모듈에서 제공됩니다.
이 모듈에서, 공유 메모리는 “POSIX 스타일” 공유 메모리 블록을 가리키며 (꼭 그런 식으로 구현돼야 할 필요는 없습니다), “분산 공유 메모리”를 가리키지는 않습니다. 이 스타일의 공유 메모리는 개별 프로세스가 잠재적으로 휘발성 메모리의 공통 (또는 공유) 영역을 읽고 쓸 수 있게 합니다. 프로세스는 일반적으로 자체 프로세스 메모리 공간에만 액세스 할 수 있도록 제한되지만, 공유 메모리는 프로세스 간에 데이터를 공유 할 수 있도록 해서, 프로세스 간에 대신 해당 데이터가 포함된 메시지를 보낼 필요가 없도록 합니다. 메모리를 통해 직접 데이터를 공유하면 디스크나 소켓 또는 직렬화/역 직렬화와 데이터의 복사를 요구하는 다른 통신과 비교하여 상당한 성능상의 이점을 얻을 수 있습니다.
- class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0, *, track=True)¶
새 공유 메모리 블록을 만들거나 기존 공유 메모리 블록에 연결하기 위해
SharedMemory
클래스의 인스턴스를 만듭니다. 각 공유 메모리 블록에는 고유한 이름이 지정됩니다. 이런 식으로, 하나의 프로세스가 특정 이름을 가진 공유 메모리 블록을 생성 할 수 있으며, 다른 프로세스가 같은 이름을 사용하여 같은 공유 메모리 블록에 연결할 수 있습니다.프로세스 간에 데이터를 공유하기 위한 자원으로서, 공유 메모리 블록은 생성한 원래 프로세스보다 오래갈 수 있습니다. 한 프로세스가 더는 다른 프로세스가 필요로 할 수도 있는 공유 메모리 블록에 대한 액세스를 필요로하지 않으면
close()
메서드를 호출해야 합니다. 어떤 프로세스에서도 공유 메모리 블록이 더는 필요하지 않으면, 적절한 정리를 위해unlink()
메서드를 호출해야 합니다.- 매개변수:
name (str | None) – 문자열로 지정된, 요청된 공유 메모리의 고유한 이름. 새 공유 메모리 블록을 만들 때, 이름에
None
(기본값)이 제공되면, 새로운 이름이 생성됩니다.create (bool) – 새 공유 메모리 블록을 만들지(
True
), 또는 기존 공유 메모리 블록을 연결할지(False
)를 제어합니다.size (int) – 새 공유 메모리 블록을 만들 때 요청된 바이트 수. 일부 플랫폼은 해당 플랫폼의 메모리 페이지 크기를 기반으로 메모리 덩어리를 할당하기 때문에, 공유 메모리 블록의 정확한 크기는 요청한 크기보다 크거나 같을 수 있습니다. 기존 공유 메모리 블록에 연결할 때는, size 매개 변수가 무시됩니다.
track (bool) – When
True
, register the shared memory block with a resource tracker process on platforms where the OS does not do this automatically. The resource tracker ensures proper cleanup of the shared memory even if all other processes with access to the memory exit without doing so. Python processes created from a common ancestor usingmultiprocessing
facilities share a single resource tracker process, and the lifetime of shared memory segments is handled automatically among these processes. Python processes created in any other way will receive their own resource tracker when accessing shared memory with track enabled. This will cause the shared memory to be deleted by the resource tracker of the first process that terminates. To avoid this issue, users ofsubprocess
or standalone Python processes should set track toFalse
when there is already another process in place that does the bookkeeping. track is ignored on Windows, which has its own tracking and automatically deletes shared memory when all handles to it have been closed.
버전 3.13에서 변경: Added the track parameter.
- close()¶
Close the file descriptor/handle to the shared memory from this instance.
close()
should be called once access to the shared memory block from this instance is no longer needed. Depending on operating system, the underlying memory may or may not be freed even if all handles to it have been closed. To ensure proper cleanup, use theunlink()
method.
- unlink()¶
Delete the underlying shared memory block. This should be called only once per shared memory block regardless of the number of handles to it, even in other processes.
unlink()
andclose()
can be called in any order, but trying to access data inside a shared memory block afterunlink()
may result in memory access errors, depending on platform.This method has no effect on Windows, where the only way to delete a shared memory block is to close all handles.
- 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]) # 여러 바이트를 한 번에 수정합니다
>>> buffer[4] = 100 # 한 번에 한 바이트씩 수정합니다
>>> # 기존 공유 메모리 블록에 연결합니다
>>> shm_b = shared_memory.SharedMemory(shm_a.name)
>>> import array
>>> array.array('b', shm_b.buf[:5]) # 새 array.array로 데이터를 복사합니다
array('b', [22, 33, 44, 55, 100])
>>> shm_b.buf[:5] = b'howdy' # shm_b를 통해 bytes를 사용해서 수정합니다
>>> bytes(shm_a.buf[:5]) # shm_a를 통해 액세스합니다
b'howdy'
>>> shm_b.close() # 각 SharedMemory 인스턴스를 닫습니다
>>> shm_a.close()
>>> shm_a.unlink() # 공유 메모리를 해제하기 위해 오직 한 번만 unlink를 호출합니다
다음 예제는 두 개의 다른 파이썬 셸에서 같은 numpy.ndarray
에 액세스하는, NumPy 배열과 함께 SharedMemory
클래스를 사용하는 실용적인 방법을 보여줍니다:
>>> # 첫 번째 파이썬 대화형 셸에서
>>> import numpy as np
>>> a = np.array([1, 1, 2, 3, 5, 8]) # 기존 NumPy 배열로 시작합니다
>>> from multiprocessing import shared_memory
>>> shm = shared_memory.SharedMemory(create=True, size=a.nbytes)
>>> # 이제 공유 메모리에 NumPy 배열을 만듭니다
>>> b = np.ndarray(a.shape, dtype=a.dtype, buffer=shm.buf)
>>> b[:] = a[:] # 원래 데이터를 공유 메모리로 복사합니다
>>> b
array([1, 1, 2, 3, 5, 8])
>>> type(b)
<class 'numpy.ndarray'>
>>> type(a)
<class 'numpy.ndarray'>
>>> shm.name # 이름을 지정하지 않았으므로, 우리를 위해 이름이 선택되었습니다
'psm_21467_46075'
>>> # 같은 셸이나 같은 기계 상의 새로운 파이썬 셀에서
>>> import numpy as np
>>> from multiprocessing import shared_memory
>>> # 기존 공유 메모리 블록에 연결합니다
>>> existing_shm = shared_memory.SharedMemory(name='psm_21467_46075')
>>> # 이 예제에서 a.shape은 (6,)이고 a.dtype은 np.int64 입니다
>>> 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])
>>> # 첫 번째 파이썬 대화형 셸로 돌아가면, b가 이 변경을 반영합니다
>>> b
array([ 1, 1, 2, 3, 5, 888])
>>> # 두 번째 파이썬 셀 내에서 정리합니다
>>> del c # 필요 없습니다; 단순히 배열이 더는 사용되지 않는다는 것을 강조하기 위함입니다
>>> existing_shm.close()
>>> # 첫 번째 파이썬 셀 내에서 정리합니다
>>> del b # 필요 없습니다; 단순히 배열이 더는 사용되지 않는다는 것을 강조하기 위함입니다
>>> shm.close()
>>> shm.unlink() # 제일 끝에서 공유 메모리 블록을 해제하고 반납합니다
- class multiprocessing.managers.SharedMemoryManager([address[, authkey]])¶
프로세스 간 공유 메모리 블록을 관리하는 데 사용할 수 있는
multiprocessing.managers.BaseManager
의 서브 클래스.SharedMemoryManager
인스턴스에서start()
를 호출하면 새 프로세스가 시작됩니다. 이 새로운 프로세스의 유일한 목적은 이를 통해 생성된 모든 공유 메모리 블록의 수명 주기를 관리하는 것입니다. 해당 프로세스가 관리하는 모든 공유 메모리 블록의 해제를 시작시키려면, 해당 인스턴스에서shutdown()
을 호출하십시오. 그러면 이 프로세스에 의해 관리되는 모든SharedMemory
객체에 대해unlink()
호출을 일으키고, 그런 다음 프로세스 자체를 중지합니다.SharedMemoryManager
를 통해SharedMemory
인스턴스를 생성함으로써, 공유 메모리 자원을 수동으로 추적하여 해제할 필요가 없습니다.이 클래스는
SharedMemory
인스턴스를 만들고 반환하는 메서드와, 공유 메모리로 지원되는 리스트류 객체(ShareableList
)를 만드는 메서드를 제공합니다.상속된 address와 authkey 선택적 입력 인자에 대한 설명과 이 인자를 사용하여 다른 프로세스의 기존
SharedMemoryManager
서비스에 연결하는 방법에 대해서는BaseManager
를 참조하십시오.- SharedMemory(size)¶
바이트로 지정된 size 크기의 새로운
SharedMemory
객체를 만들고 반환합니다.
- ShareableList(sequence)¶
입력 sequence의 값으로 초기화된, 새
ShareableList
객체를 만들고 반환합니다.
다음 예제는 SharedMemoryManager
의 기본 메커니즘을 보여줍니다:
>>> from multiprocessing.managers import SharedMemoryManager
>>> smm = SharedMemoryManager()
>>> smm.start() # 공유 메모리 블록을 관리하는 프로세스를 시작합니다
>>> 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() # sl, raw_shm, 및 another_sl 에 대해 unlink()를 호출합니다
다음 예제는 with
문을 통해 SharedMemoryManager
객체를 사용하여 더는 필요하지 않은 모든 공유 메모리 블록이 해제되도록 하는, 잠재적으로 더 편리한 패턴을 보여줍니다:
>>> with SharedMemoryManager() as smm:
... sl = smm.ShareableList(range(2000))
... # 작업을 두 프로세스로 나누고, 부분적인 결과를 sl에 저장합니다
... p1 = Process(target=do_work, args=(sl, 0, 1000))
... p2 = Process(target=do_work, args=(sl, 1000, 2000))
... p1.start()
... p2.start() # multiprocessing.Pool이 더 효율적일 수 있습니다
... p1.join()
... p2.join() # 두 프로세스가 모두 작업을 끝낼 때까지 기다립니다
... total_result = sum(sl) # 이제 sl에 있는 부분 결과들을 통합합니다
with
문에서 SharedMemoryManager
를 사용할 때, with
문의 코드 블록 실행이 완료되면 해당 관리자를 사용하여 만들어진 공유 메모리 블록이 모두 해제됩니다.
- class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)¶
Provide a mutable list-like object where all values stored within are stored in a shared memory block. This constrains storable values to the following built-in data types:
int
(부호있는 64-비트)str
(less than 10M bytes each when encoded as UTF-8)bytes
(less than 10M bytes each)None
It also notably differs from the built-in
list
type in that these lists can not change their overall length (i.e. noappend()
,insert()
, etc.) and do not support the dynamic creation of newShareableList
instances via slicing.sequence는 새로운
ShareableList
를 값으로 가득 채우는 데 사용됩니다. 고유한 공유 메모리 이름으로 이미 존재하는ShareableList
에 대신 연결하려면None
으로 설정하십시오.name은
SharedMemory
에 대한 정의에서 설명한 대로, 요청된 공유 메모리의 고유한 이름입니다. 기존ShareableList
에 연결할 때, sequence를None
으로 설정하고 공유 메모리 블록의 고유한 이름을 지정하십시오.참고
A known issue exists for
bytes
andstr
values. If they end with\x00
nul bytes or characters, those may be silently stripped when fetching them by index from theShareableList
. This.rstrip(b'\x00')
behavior is considered a bug and may go away in the future. See gh-106939.For applications where rstripping of trailing nulls is a problem, work around it by always unconditionally appending an extra non-0 byte to the end of such values when storing and unconditionally removing it when fetching:
>>> 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)¶
value의 발생 횟수를 반환합니다.
- index(value)¶
value의 첫 번째 인덱스 위치를 반환합니다. value가 없으면
ValueError
를 발생시킵니다.
- 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()
The following examples demonstrates that ShareableList
(and underlying SharedMemory
) objects
can be pickled and unpickled if needed.
Note, that it will still be the same shared object.
This happens, because the deserialized object has
the same unique name and is just attached to an existing
object with the same name (if the object is still alive):
>>> 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()