PHpullh
학습 라이브러리/Python/동시성 — multiprocessing & concurrent.futures

PYTHON · 비동기

동시성 — multiprocessing & concurrent.futures

GIL 때문에 CPU 바운드 작업은 threading으로 병렬화되지 않습니다. multiprocessing이나 concurrent.futures.ProcessPoolExecutor를 사용하면 멀티코어를 활용한 진정한 병렬 처리가 가능합니다.

비동기중급multiprocessingconcurrent-futuresparallelismGIL

핵심 설명

GIL 때문에 CPU 바운드 작업은 threading으로 병렬화되지 않습니다. multiprocessing이나 concurrent.futures.ProcessPoolExecutor를 사용하면 멀티코어를 활용한 진정한 병렬 처리가 가능합니다.

Python code

import time
import math
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed

def is_prime(n):
    """CPU 바운드 작업: 소수 판별"""
    if n < 2:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True

def count_primes(start, end):
    """범위 내 소수 개수 계산"""
    return sum(1 for n in range(start, end) if is_prime(n))

def benchmark(executor_class, name, ranges):
    start = time.perf_counter()
    results = []
    with executor_class(max_workers=4) as executor:
        futures = {
            executor.submit(count_primes, s, e): (s, e)
            for s, e in ranges
        }
        for future in as_completed(futures):
            s, e = futures[future]
            count = future.result()
            results.append(count)
    elapsed = time.perf_counter() - start
    total = sum(results)
    print(f"  {name}: {total}개 소수, {elapsed:.3f}초")
    return elapsed

if __name__ == '__main__':
    RANGE = 500_000
    CHUNKS = 4
    chunk_size = RANGE // CHUNKS
    ranges = [(i * chunk_size, (i+1) * chunk_size) for i in range(CHUNKS)]

    print(f"0~{RANGE:,} 범위 소수 계산 (4 workers):")

    # 순차 실행
    start = time.perf_counter()
    total = count_primes(0, RANGE)
    seq_time = time.perf_counter() - start
    print(f"  순차 실행: {total}개 소수, {seq_time:.3f}초")

    # 멀티프로세스 (CPU 바운드에 효과적)
    proc_time = benchmark(ProcessPoolExecutor, "프로세스풀", ranges)

    # 멀티스레드 (GIL로 인해 CPU 바운드에 비효율)
    thread_time = benchmark(ThreadPoolExecutor, "스레드풀  ", ranges)

    print(f"\n속도 향상: 프로세스풀 {seq_time/proc_time:.1f}x")

학습 팁

ProcessPoolExecutor는 프로세스 간 데이터를 pickle로 직렬화합니다. 큰 데이터는 공유 메모리(multiprocessing.shared_memory)를 사용하세요.

주의할 점

Windows에서 multiprocessing 사용 시 if __name__ == "__main__": 가드가 없으면 무한 재귀로 프로세스가 폭발합니다.

자주 묻는 질문

동시성 — multiprocessing & concurrent.futures란 무엇인가요?

GIL 때문에 CPU 바운드 작업은 threading 으로 병렬화되지 않습니다. multiprocessing 이나 concurrent.futures.ProcessPoolExecutor 를 사용하면 멀티코어를 활용한 진정한 병렬 처리가 가능합니다.

동시성 — multiprocessing & concurrent.futures 학습 시 주의할 점은 무엇인가요?

Windows에서 multiprocessing 사용 시 if __name__ == "__main__": 가드가 없으면 무한 재귀로 프로세스가 폭발합니다.