개발/Python

[중급반] Step 5. 병렬성과 동시성: Multiprocessing과 Asyncio

ophelisis 2025. 12. 26. 14:31
반응형

Step 5에서는 파이썬의 성능을 극대화하는 **병렬성(Parallelism)**과 **동시성(Concurrency)**을 다룹니다. 파이썬은 GIL(Global Interpreter Lock)이라는 제약이 있지만, 이를 우회하여 여러 작업을 동시에 처리하는 다양한 전략을 가지고 있습니다. CPU 집약적인 작업과 I/O 집약적인 작업을 어떻게 다르게 처리해야 하는지 배워봅시다.


1. 🚦 파이썬의 제약: GIL (Global Interpreter Lock)

파이썬(CPython)은 한 번에 하나의 스레드만 파이썬 바이트코드를 실행하도록 설계되어 있습니다. 이를 GIL이라고 합니다.

  • 영향: 멀티 코어 환경에서도 멀티스레딩만으로는 CPU 연산 성능을 온전히 끌어올리기 어렵습니다.
  • 해결책: CPU 연산이 많은 작업은 Multiprocessing을, 네트워크 요청 등 대기가 많은 작업은 AsyncioThreading을 사용합니다.

2. 🏗️ Multiprocessing: 진정한 병렬 처리

multiprocessing 모듈은 GIL을 우회하기 위해 여러 개의 프로세스를 생성합니다. 각 프로세스는 고유한 메모리 공간과 별도의 GIL을 가집니다.

  • 용도: 데이터 분석, 이미지 처리, 암호화 등 CPU 연산이 집약적인 작업.
  • 특징: 프로세스 간 통신(IPC) 비용이 발생하지만, 멀티 코어를 100% 활용할 수 있습니다.
Python
 
from multiprocessing import Process, current_process

def work():
    print(f"작업 프로세스: {current_process().name}")

if __name__ == "__main__":
    processes = []
    for i in range(4): # 4개의 프로세스 생성
        p = Process(target=work)
        processes.append(p)
        p.start()

    for p in processes:
        p.join()

3. ⚡ Asyncio: 단일 스레드 동시성

asyncio는 async와 await 키워드를 사용하여 비동기 프로그래밍을 지원합니다. 이벤트 루프(Event Loop)가 여러 작업을 전환하며 대기 시간을 효율적으로 활용합니다.

  • 용도: 웹 크롤링, API 요청, 파일 입출력 등 네트워크/디스크 대기가 많은(I/O Bound) 작업.
  • 특징: 스레드 스위칭 비용이 없어 매우 빠르고 가볍습니다.
Python
 
import asyncio

async def fetch_data():
    print("데이터 요청 시작...")
    await asyncio.sleep(2) # 비동기 대기
    print("데이터 수신 완료!")
    return {"data": 123}

async def main():
    # 여러 비동기 작업을 동시에 실행
    result = await asyncio.gather(fetch_data(), fetch_data())
    print(result)

asyncio.run(main())

💡 시니어의 조언: 도구 선택의 기준

  • CPU가 바쁘다(연산 위주): Multiprocessing을 선택하세요.
  • CPU가 노는 시간이 많다(네트워크 대기 위주): Asyncio가 가장 효율적입니다.
  • 단순히 여러 작업을 처리하고 싶다: 상황에 따라 Threading을 고려하되, 스레드 안전성(Thread Safety)에 주의해야 합니다.
반응형