과거 파이썬에서 동시성이나 병렬 처리를 구현하려면 threading
이나 multiprocessing
모듈을 직접 다루며 복잡한 스레드 생성, 락(Lock) 관리, 프로세스간 통신(IPC) 코드를 작성해야 했습니다. 이는 코드가 장황해지고 디버깅이 어려워지는 문제를 일으켰습니다.
Python 3.2+에서 도입된 concurrent.futures
모듈과 3.4+의 asyncio
는 이러한 문제를 완화하고, 더 직관적이고 Pythonic한 방식으로 동시성 작업을 처리할 수 있게 합니다.
이번 글에서는 기존 접근법과 새로운 접근법을 비교하고, 각 방법의 장단점을 정리합니다.
이전에는 어떻게 했을까?
threading, multiprocessing
import threading
def worker():
# 작업 처리
pass
t = threading.Thread(target=worker)
t.start()
t.join()
단점:
- 스레드 관리, 락 획득/해제 등 동기화 로직 작성 필요
- 코드 복잡성 증가, 디버깅 어려움
multiprocessing
도 프로세스 기반 병렬처리를 지원하지만, 큐나 파이프 등 IPC를 명시적으로 다루어야 하고, 코드가 복잡해질 수 있음.
concurrent.futures: 고급 추상화 제공
concurrent.futures
모듈의 ThreadPoolExecutor
나 ProcessPoolExecutor
를 사용하면 스레드/프로세스풀 관리가 쉬워집니다.
from concurrent.futures import ThreadPoolExecutor
def worker():
# 일부 계산 작업
return 42
with ThreadPoolExecutor(max_workers=4) as executor:
future = executor.submit(worker)
result = future.result() # 완료 시 결과 반환
장점:
- 스레드나 프로세스풀을 명시적으로 관리 필요 없음
future
객체로 비동기 결과 핸들링 용이- 코드 가독성↑, 간단한 API로 병렬 처리 구현
단점:
- 여전히 GIL(Global Interpreter Lock)의 영향으로 CPU 바운드 작업 시
ThreadPoolExecutor
제한. CPU 바운드 작업은ProcessPoolExecutor
나 다른 전략 필요 - 매우 복잡한 동시성 로직은 추가 관리 필요
asyncio: 비동기 I/O 기반 동시성
asyncio
는 async/await 문법을 통해 비동기 I/O를 손쉽게 처리할 수 있습니다. 특히 네트워크 요청, 파일 I/O 등 I/O 바운드 작업에 뛰어난 성능 향상을 기대할 수 있습니다.
import asyncio
async def fetch_data():
await asyncio.sleep(1) # 비동기 I/O 모사
return "data"
async def main():
result = await fetch_data()
print(result)
asyncio.run(main())
장점:
- async/await 문법으로 비동기 코드를 동기 코드처럼 작성 가능 (제어 흐름 명확)
- I/O 바운드 작업 성능 개선
- 에코 서버, 웹 크롤러, 비동기 DB 접근 등에 활용성↑
단점:
- async/await 패러다임 이해 필요
- 기존 동기 코드와 섞어 사용할 때 적응 필요
성능 측면
- I/O 바운드 작업:
asyncio
를 사용하면 스레드보다 효율적으로 수천 개의 동시 연결 처리 가능 - CPU 바운드 작업:
ProcessPoolExecutor
나 multiprocessing 필요. asyncio는 I/O 바운드에 초점 - 적절한 도구 선택이 핵심: CPU 바운드면 multiprocessing, I/O 바운드면 asyncio로 분명한 전략 수립 가능
결론
- 이전 방식: threading, multiprocessing 직접 사용 → 락 관리, IPC로 코드 복잡
- 새로운 방식:
concurrent.futures
로 스레드/프로세스풀 관리 간소화,asyncio
로 비동기 I/O에 최적화 - 장점: 가독성 증가, 코드 간결, 성능 개선(I/O 바운드), 유지보수성↑
- 단점: async/await 문법 학습 필요, CPU 바운드 작업은 별도 전략 필요
다음 글에서는 패키징, 빌드, 의존성 관리를 현대적으로 해결하는 pyproject.toml
와 Poetry를 소개하여, 기존 setup.py
/requirements.txt
방식 대비 훨씬 깔끔한 프로젝트 구성 방법을 알아보겠습니다.
'개발 이야기 > Python (파이썬)' 카테고리의 다른 글
[모던 Python 7편] 패턴 매칭(Pattern Matching)으로 조건문 단순화하기 (0) | 2024.12.17 |
---|---|
[모던 Python 6편] pyproject.toml와 Poetry로 패키징 현대화하기 (0) | 2024.12.17 |
[모던 Python 4편] pathlib로 직관적인 파일 경로 처리하기 (1) | 2024.12.17 |
[모던 Python 3편] dataclass로 데이터 구조 간소화하기 (0) | 2024.12.17 |
[모던 Python 2편] 타입 힌트와 Mypy로 코드 명확성 강화하기 (0) | 2024.12.17 |