multiprocessing — 프로세스 기반 병렬성

multiprocessing — 프로세스 기반 병렬성

multiprocessingthreading 모듈과 비슷한 API 로 프로세스를 생성하는 것을 지원하는 패키지예요. 이 패키지는 로컬 및 원격 동시성을 모두 제공하며, 스레드 대신 하위 프로세스를 사용해 전역 인터프리터 잠금(Global Interpreter Lock)을 효과적으로 우회해요. 덕분에 지정된 머신의 여러 프로세서를 완전히 활용할 수 있어요. POSIX 와 Windows 모두에서 실행돼요.

출처: Python documentation

본문

가용성: Android, iOS, WASI 아님. 이 모듈은 모바일 플랫폼이나 WebAssembly 플랫폼에서는 지원되지 않아요.

소개 (Introduction)

multiprocessing 모듈은 또한 여러 입력 값에 걸쳐 함수 실행을 병렬화하고 입력 데이터를 프로세스들에 분배(데이터 병렬성)하는 편리한 수단을 제공하는 Pool 객체를 도입해요. 자식 프로세스가 그 모듈을 성공적으로 import 할 수 있도록 함수를 모듈에 정의하는 일반적인 관행의 기본 데이터 병렬성 예제:

from multiprocessing import Pool

def f(x):
    return x*x

if __name__ == '__main__':
    with Pool(5) as p:
        print(p.map(f, [1, 2, 3]))

표준 출력에 [1, 4, 9] 를 출력해요.

multiprocessing 모듈은 또한 실행 중인 프로세스를 terminate, interrupt 또는 kill 하는 능력처럼 threading 모듈에는 대응물이 없는 API 를 도입해요.

concurrent.futures.ProcessPoolExecutor 는 호출 프로세스의 실행을 차단하지 않고 백그라운드 프로세스에 작업을 밀어 넣는 더 높은 레벨 인터페이스를 제공해요. Pool 인터페이스를 직접 쓰는 것과 비교해, concurrent.futures API 는 기본 프로세스 풀에 작업을 제출하는 것과 결과를 기다리는 것을 더 쉽게 분리할 수 있어요.

Process 클래스 (The Process class)

multiprocessing 에서 프로세스는 Process 객체를 만들고 그 start() 메서드를 호출해 생성돼요. Processthreading.Thread 의 API 를 따르고, target, args, name 등의 인자를 받아요.

from multiprocessing import Process

def f(name):
    print('hello', name)

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

Process 의 인자는 보통 피클 가능(picklable)해야 하므로 자식 프로세스에 전달할 수 있어요. if __name__ == '__main__' 부분이 필요한 이유에 대해서는 프로그래밍 지침을 참고하세요.

컨텍스트와 시작 방법 (Contexts and start methods)

플랫폼에 따라 multiprocessing 은 프로세스를 시작하는 세 가지 방법을 지원해요.

spawn — 부모 프로세스가 새 파이썬 인터프리터 프로세스를 시작해요. 자식 프로세스는 프로세스 객체의 run() 메서드를 실행하는 데 필요한 자원만 상속해요. 특히 부모의 불필요한 파일 디스크립터와 핸들은 상속되지 않아요. 이 방법은 forkforkserver 에 비해 꽤 느려요. POSIX 와 Windows 에서 사용 가능하며 Windows 와 macOS 의 기본값이에요.

fork — 부모 프로세스가 os.fork() 로 파이썬 인터프리터를 fork 해요. 자식 프로세스는 시작될 때 사실상 부모와 동일해요. 부모의 모든 자원이 자식에 상속돼요. 멀티스레드 프로세스를 안전하게 fork 하는 것은 문제가 있을 수 있어요. POSIX 시스템에서 사용 가능. 버전 3.14부터 어느 플랫폼에서도 기본 시작 방법이 아니게 됐어요.

forkserver — 프로그램이 시작될 때 단일 서버 프로세스가 시작돼요. 이후 어떤 프로세스를 시작해야 할 때마다 서버가 새 프로세스를 fork 해요.

주요 클래스와 API

  • **multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None) — 프로세스 객체. start(), join(), terminate(), kill(), is_alive() 등의 메서드를 제공해요.
  • multiprocessing.Pool([processes[, initializer[, initargs[, maxtasksperchild[, context]]]]]) — 프로세스 풀. apply(), apply_async(), map(), map_async(), starmap(), imap() 등의 메서드를 제공해요.
  • multiprocessing.Queue([maxsize]) — 다중 프로세스 큐. put(), get() 등을 지원해요.
  • multiprocessing.Pipe([duplex]) — 두 프로세스 간 양방향 통신용 연결 쌍을 반환해요.
  • multiprocessing.Lock(), RLock(), Semaphore([value]), Event(), Condition([lock]), Barrier([parties]) — 스레딩의 잠금·동기화 프리미티브와 대응하는 프로세스 버전.
  • multiprocessing.Value(typecode_or_type, args, lock=True) — 프로세스 간 공유 메모리 값.
  • multiprocessing.Array(_typecode_or_type, size_or_initializer, lock=True)* — 공유 메모리 배열.
  • multiprocessing.Manager() — 공유 객체를 관리하는 매니저 프로세스.
  • multiprocessing.get_context(method=None), set_start_method(method) — 시작 방법을 조회·설정.
  • multiprocessing.connection — 프로세스 간 통신용 연결 객체(Listener, Client).
  • multiprocessing.dummymultiprocessing 의 API 를 스레드 위에 제공하는 모듈.

프로그래밍 지침 (Programming guidelines)

  • 모든 시작 방법에서: 전역 상태 사용 피하기, 자식에 전달되는 인자는 피클 가능하게, if __name__ == '__main__': 가드 사용.
  • spawnforkserver 시작 방법에서: 자식 프로세스가 모듈을 안전하게 import 할 수 있어야 하며, 함수·클래스를 모듈 최상위에 정의해 피클 가능하게 해야 해요.

예제 (Examples)

multiprocessing 문서에는 프로세스, 풀, 파이프, 큐, 매니저를 사용하는 다양한 예제가 제공돼요. 멀티코어 CPU를 활용한 병렬 처리에서 널리 쓰이는 표준 라이브러리예요.

더 알아보기 (Learn more)