multiprocessing.shared_memory — Shared memory for direct access across processes

소스 코드: Lib/multiprocessing/shared_memory.py

버전 3.8에 추가.


이 모듈은 멀티 코어나 대칭 멀티 프로세서 (SMP) 기계에서 하나 이상의 프로세스가 액세스할 공유 메모리의 할당과 관리를 위한 클래스 SharedMemory를 제공합니다. 특히 별개의 프로세스에 걸친 공유 메모리의 수명 주기 관리를 지원하기 위해, BaseManager 서브 클래스인 SharedMemoryManagermultiprocessing.managers 모듈에서 제공됩니다.

이 모듈에서, 공유 메모리는 “시스템 V 스타일” 공유 메모리 블록을 가리키며 (꼭 그런 식으로 구현돼야 할 필요는 없습니다), “분산 공유 메모리”를 가리키지는 않습니다. 이 스타일의 공유 메모리는 개별 프로세스가 잠재적으로 휘발성 메모리의 공통 (또는 공유) 영역을 읽고 쓸 수 있게 합니다. 프로세스는 일반적으로 자체 프로세스 메모리 공간에만 액세스 할 수 있도록 제한되지만, 공유 메모리는 프로세스 간에 데이터를 공유 할 수 있도록 해서, 프로세스 간에 대신 해당 데이터가 포함된 메시지를 보낼 필요가 없도록 합니다. 메모리를 통해 직접 데이터를 공유하면 디스크나 소켓 또는 직렬화/역 직렬화와 데이터의 복사를 요구하는 다른 통신과 비교하여 상당한 성능상의 이점을 얻을 수 있습니다.

class multiprocessing.shared_memory.SharedMemory(name=None, create=False, size=0)

새 공유 메모리 블록을 만들거나 기존 공유 메모리 블록에 연결합니다. 각 공유 메모리 블록에는 고유한 이름이 지정됩니다. 이런 식으로, 하나의 프로세스가 특정 이름을 가진 공유 메모리 블록을 생성 할 수 있으며, 다른 프로세스가 같은 이름을 사용하여 같은 공유 메모리 블록에 연결할 수 있습니다.

프로세스 간에 데이터를 공유하기 위한 자원으로서, 공유 메모리 블록은 생성한 원래 프로세스보다 오래갈 수 있습니다. 한 프로세스가 더는 다른 프로세스가 필요로 할 수도 있는 공유 메모리 블록에 대한 액세스를 필요로하지 않으면 close() 메서드를 호출해야 합니다. 어떤 프로세스에서도 공유 메모리 블록이 더는 필요하지 않으면, 적절한 정리를 위해 unlink() 메서드를 호출해야 합니다.

name은 문자열로 지정된 요청된 공유 메모리의 고유한 이름입니다. 새 공유 메모리 블록을 만들 때, 이름에 None(기본값)이 제공되면, 새로운 이름이 생성됩니다.

create는 새 공유 메모리 블록을 만들지(True), 또는 기존 공유 메모리 블록을 연결할지(False)를 제어합니다.

size는 새 공유 메모리 블록을 만들 때 요청된 바이트 수를 지정합니다. 일부 플랫폼은 해당 플랫폼의 메모리 페이지 크기를 기반으로 메모리 덩어리를 할당하기 때문에, 공유 메모리 블록의 정확한 크기는 요청한 크기보다 크거나 같을 수 있습니다. 기존 공유 메모리 블록에 연결할 때는, size 매개 변수가 무시됩니다.

close()

이 인스턴스에서 공유 메모리에 대한 액세스를 닫습니다. 자원을 적절히 정리하기 위해, 인스턴스가 더는 필요하지 않으면 모든 인스턴스가 close()를 호출해야 합니다. close()를 호출해도 공유 메모리 블록 자체가 파괴되지는 않습니다.

하부 공유 메모리 블록이 삭제되도록 요청합니다. 리소스를 적절히 정리하려면, unlink()를 공유 메모리 블록이 필요한 모든 프로세스 전체에서 (오직) 한 번만 호출해야 합니다. 파괴를 요청한 후에는, 공유 메모리 블록이 즉시 파괴될 수도 있고 그렇지 않을 수도 있습니다. 이 동작은 플랫폼에 따라 다를 수 있습니다. unlink()가 호출된 후에, 공유 메모리 블록 내부의 데이터에 액세스하려고 하면 메모리 액세스 에러가 발생할 수 있습니다. 주의: 공유 메모리 블록에 대한 참조를 해제하는 마지막 프로세스는 unlink()close()를 어느 순서로든 호출 할 수 있습니다.

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])  # 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]])

프로세스 간 공유 메모리 블록을 관리하는 데 사용할 수 있는 BaseManager의 서브 클래스.

SharedMemoryManager 인스턴스에서 start()를 호출하면 새 프로세스가 시작됩니다. 이 새로운 프로세스의 유일한 목적은 이를 통해 생성된 모든 공유 메모리 블록의 수명 주기를 관리하는 것입니다. 해당 프로세스가 관리하는 모든 공유 메모리 블록의 해제를 시작시키려면, 해당 인스턴스에서 shutdown()을 호출하십시오. 그러면 이 프로세스에 의해 관리되는 모든 SharedMemory 객체에 대해 SharedMemory.unlink() 호출을 일으키고, 그런 다음 프로세스 자체를 중지합니다. SharedMemoryManager를 통해 SharedMemory 인스턴스를 생성함으로써, 공유 메모리 자원을 수동으로 추적하여 해제할 필요가 없습니다.

이 클래스는 SharedMemory 인스턴스를 만들고 반환하는 메서드와, 공유 메모리로 지원되는 리스트류 객체(ShareableList)를 만드는 메서드를 제공합니다.

상속된 addressauthkey 선택적 입력 인자에 대한 설명과 이 인자를 사용하여 다른 프로세스의 기존 SharedMemoryManager 서비스에 연결하는 방법에 대해서는 multiprocessing.managers.BaseManager를 참조하십시오.

SharedMemory(size)

바이트로 지정된 size 크기의 새로운 SharedMemory 객체를 만들고 반환합니다.

ShareableList(sequence)

입력 sequence의 값으로 초기화된, 새 ShareableList 객체를 만들고 반환합니다.

다음 예제는 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

다음 예제는 with 문을 통해 SharedMemoryManager 객체를 사용하여 더는 필요하지 않은 모든 공유 메모리 블록이 해제되도록 하는, 잠재적으로 더 편리한 패턴을 보여줍니다:

>>> 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

with 문에서 SharedMemoryManager를 사용할 때, with 문의 코드 블록 실행이 완료되면 해당 관리자를 사용하여 만들어진 공유 메모리 블록이 모두 해제됩니다.

class multiprocessing.shared_memory.ShareableList(sequence=None, *, name=None)

안에 저장되는 모든 값이 공유 메모리 블록에 저장되는 가변 리스트류 객체를 제공합니다. 이것은 int, float, bool, str (각각 10M 바이트 미만), bytes (각각 10M 바이트 미만) 및 None 내장 데이터형으로만 저장 가능한 값을 제한합니다. 또한, 이 리스트는 전체 길이를 변경할 수 없으며 (즉, 추가, 삽입 등이 없습니다), 슬라이싱을 통해 새로운 ShareableList 인스턴스를 동적으로 생성할 수 없다는 점에서 내장 list 형과 상당히 다릅니다.

sequence는 새로운 ShareableList를 값으로 가득 채우는 데 사용됩니다. 고유한 공유 메모리 이름으로 이미 존재하는 ShareableList에 대신 연결하려면 None으로 설정하십시오.

nameSharedMemory에 대한 정의에서 설명한 대로, 요청된 공유 메모리의 고유한 이름입니다. 기존 ShareableList에 연결할 때, sequenceNone으로 설정하고 공유 메모리 블록의 고유한 이름을 지정하십시오.

count(value)

value의 발생 횟수를 반환합니다.

index(value)

value의 첫 번째 인덱스 위치를 반환합니다. value가 없으면 ValueError를 발생시킵니다.

format

현재 저장된 모든 값이 사용하는 struct 패킹 형식을 포함하는 읽기 전용 어트리뷰트.

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()