# `multiprocessing.shared_memory` — 프로세스 간 직접 액세스를 위한 공유 메모리를 제공합니다

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

버전 3.8에 추가.

이 모듈은 멀티 코어나 대칭 멀티 프로세서 (SMP) 기계에서 하나 이상의 프로세스가 액세스할 공유 메모리의 할당과 관리를 위한 클래스 `SharedMemory`를 제공합니다. 특히 별개의 프로세스에 걸친 공유 메모리의 수명 주기 관리를 지원하기 위해, `BaseManager` 서브 클래스인 `SharedMemoryManager`도 `multiprocessing.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()`가 호출된 후에, 공유 메모리 블록 내부의 데이터에 액세스하려고 하면 메모리 액세스 에러가 발생할 수 있습니다. 주의: 공유 메모리 블록에 대한 참조를 해제하는 마지막 프로세스는 `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

다음 예제는 두 개의 다른 파이썬 셸에서 같은 `numpy.ndarray`에 액세스하는, NumPy 배열과 함께 `SharedMemory` 클래스를 사용하는 실용적인 방법을 보여줍니다:

    >>> # 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`)를 만드는 메서드를 제공합니다.

  상속된 address와 authkey 선택적 입력 인자에 대한 설명과 이 인자를 사용하여 다른 프로세스의 기존 `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`으로 설정하십시오.

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

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