본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] threading과 multiprocessing의 2가지 핵심 차이와 상황별 선택 방법

by Papa Martino V 2026. 3. 17.
728x90

threading과 multiprocessing
threading과 multiprocessing

 

파이썬으로 고성능 애플리케이션을 개발할 때 가장 먼저 맞닥뜨리는 고민은 "병렬 처리를 어떻게 구현할 것인가?"입니다. 특히 threading(스레딩)multiprocessing(멀티프로세싱)은 비슷해 보이지만, 파이썬의 독특한 구조인 GIL(Global Interpreter Lock) 때문에 그 결과가 극명하게 갈립니다. 본 포스팅에서는 I/O Bound와 CPU Bound 작업의 본질적인 차이를 분석하고, 시스템 자원을 최적으로 활용하기 위한 명확한 해결 방법을 제시합니다.


1. GIL(Global Interpreter Lock)과 파이썬의 병렬성

파이썬(CPython)은 한 번에 하나의 스레드만 파이썬 바이트코드를 실행할 수 있도록 제한하는 GIL을 가지고 있습니다. 이 때문에 멀티 스레드를 사용하더라도 CPU 연산 집약적인 작업에서는 성능 향상을 기대하기 어렵습니다. 반면, 멀티프로세싱은 각각의 프로세스가 독립된 메모리 공간과 별도의 GIL을 가지므로 진정한 의미의 병렬 처리가 가능해집니다.

2. 작업 유형별 선택 가이드 및 비교 차이점

내가 작성한 코드가 네트워크 통신을 기다리는지, 아니면 복잡한 수학 계산을 수행하는지에 따라 선택지가 달라집니다.

비교 항목 threading (스레딩) multiprocessing (멀티프로세싱) 성능 및 리소스 영향
주요 대상 I/O Bound (파일, 네트워크) CPU Bound (행렬 연산, 암호화) 작업 성격에 따른 효율 차이
메모리 공유 프로세스 내 자원 공유 가능 독립적인 메모리 공간 (IPC 필요) 스레딩이 메모리 오버헤드 적음
GIL 영향 GIL에 의해 실행 차단됨 GIL 우회 가능 (병렬 실행) CPU 연산 시 프로세싱이 우세
오버헤드 생성 및 전환 속도 빠름 생성 비용이 높고 리소스 많이 사용 대량 작업 시 스레딩이 가벼움

3. 성능 병목 해결을 위한 2가지 최적화 전략

무조건적인 병렬 도입보다는 문제의 본질을 파악하고 다음의 해결 방법을 적용해 보세요.

방법 01: I/O 대기가 많다면 threading + asyncio

API 호출이나 데이터베이스 쿼리 대기가 주를 이룬다면 스레딩을 사용하거나, 더 현대적인 방식인 asyncio를 결합하여 단일 스레드 내에서 컨텍스트 스위칭 비용을 최소화하는 것이 유리합니다.

방법 02: 데이터 연산이 많다면 multiprocessing.Pool 활용

대량의 데이터를 가공하거나 이미지 처리를 수행할 때는 multiprocessing.Pool을 통해 CPU 코어 개수만큼 프로세스를 할당하십시오. 이는 직렬 처리 대비 코어 수에 비례하는 성능 향상을 해결책으로 제공합니다.

4. 실전 샘플 예제 (Sample Example)

CPU Bound 작업을 멀티프로세싱으로 해결하여 시간을 단축하는 예제 코드입니다.


import time
from multiprocessing import Pool

# CPU 연산 집약적인 작업 예시 (제곱합 계산)
def heavy_computation(n):
    result = 0
    for i in range(n):
        result += i * i
    return result

if __name__ == "__main__":
    numbers = [10**7, 10**7, 10**7, 10**7]
    
    # 1. 순차적 처리 (동기)
    start = time.time()
    [heavy_computation(n) for n in numbers]
    print(f"순차 처리 시간: {time.time() - start:.2f}초")
    
    # 2. 멀티프로세싱 처리 (병렬)
    start = time.time()
    with Pool(processes=4) as pool:
        pool.map(heavy_computation, numbers)
    print(f"멀티프로세싱 시간: {time.time() - start:.2f}초")

결과 분석: 위 코드를 실행하면 멀티프로세싱 방식이 순차적 방식보다 약 3~4배(4코어 기준) 빠르게 완료됨을 확인할 수 있습니다.

5. 결론 및 요약

결론적으로 "데이터 전송과 대기"가 핵심이라면 threading을, "복잡한 계산과 로직 실행"이 핵심이라면 multiprocessing을 선택하는 것이 정답입니다. 파이썬 3.12 이후 버전에서는 GIL의 제약을 완화하려는 시도가 계속되고 있지만, 현재까지의 실무 환경에서는 이 두 라이브러리의 차이를 명확히 인지하고 적용하는 것이 시니어 개발자로 가는 첫걸음입니다.


내용 출처 및 참고 문헌

  • Python Documentation: "threading — Thread-based parallelism"
  • Python Documentation: "multiprocessing — Process-based parallelism"
  • David Beazley, "Understanding the Python GIL."
  • High Performance Python (2nd Edition) by Micha Gorelick and Ian Ozsvald.
728x90