
파이썬으로 동시성 프로그래밍을 배우다 보면 반드시 마주치는 질문이 하나 있습니다. "스레드를 쓸까, 프로세스를 쓸까?" 이 선택은 단순한 취향의 문제가 아니라, 여러분이 처리하려는 작업이 CPU 바운드인지 I/O 바운드인지에 따라 성능이 크게 갈리는 실질적인 문제입니다. 이 글에서는 멀티프로세싱과 멀티스레딩의 개념 차이, GIL의 영향, 그리고 실무에서의 선택 기준을 정리합니다.
1. 기본/개요
파이썬의 기본 구현체인 CPython에는 GIL(Global Interpreter Lock)이라는 메커니즘이 있습니다. GIL은 한 프로세스 안에서 오직 하나의 스레드만 파이썬 바이트코드를 실행할 수 있도록 제한합니다. 멀티코어 CPU를 쓰고 있어도, 순수 파이썬 코드를 여러 스레드로 나눠 돌린다고 해서 진짜 병렬로 실행되는 것은 아니라는 뜻입니다.
멀티스레딩(threading)은 하나의 프로세스 안에서 여러 스레드가 메모리를 공유하며 실행되는 방식입니다. GIL 때문에 CPU 연산 자체는 동시에 진행되지 않지만, 파일 읽기나 네트워크 요청처럼 I/O를 기다리는 동안에는 GIL이 해제되므로 다른 스레드가 그 틈에 실행될 수 있습니다.
멀티프로세싱(multiprocessing)은 아예 별도의 파이썬 인터프리터를 여러 개 띄우는 방식입니다. 각 프로세스는 독립된 메모리 공간과 자신만의 GIL을 가지므로, 여러 CPU 코어에서 진짜 병렬로 연산을 수행할 수 있습니다. 대신 프로세스 간에는 메모리를 직접 공유할 수 없어, 파이프나 큐 같은 별도의 통신 수단을 통해 데이터를 주고받아야 합니다.
2. 기능/스펙
threading 모듈
- Thread 객체 생성, 여러 스레드 동시 실행, Lock/Semaphore 등 동기화 도구 제공
- I/O 바운드 작업, 예를 들어 여러 파일을 동시에 읽거나 다수의 네트워크 요청을 기다리는 상황에서 효율적입니다.
- 스레드 생성 비용이 프로세스보다 가볍습니다.
multiprocessing 모듈
- Process 객체 또는 ProcessPoolExecutor로 여러 프로세스를 병렬 실행
- 이미지 처리, 대규모 수치 계산, 머신러닝 전처리처럼 CPU 연산이 지배적인 작업에서 성능 이점이 뚜렷합니다.
- 프로세스 생성 비용이 상대적으로 크기 때문에, 작업 단위가 너무 작으면 오히려 오버헤드가 이득보다 커질 수 있습니다. 보통 CPU 코어 수에 맞춰 워커 개수를 설정합니다.
최근에는 Python 3.13부터 실험적으로 도입된 free-threading(--disable-gil) 빌드도 논의되고 있습니다. 이 모드를 사용하면 GIL 없이도 진짜 스레드 병렬성을 얻을 수 있지만, 아직 실험적 단계이며 참조 카운팅 오버헤드로 인해 단일 스레드 코드의 실행 속도가 10~40%가량 느려질 수 있다는 보고도 있습니다. 이 때문에 현재까지도 GIL이 기본값으로 유지되고 있는 상황입니다.
3. 장단점
멀티스레딩 장점
- 스레드 생성과 전환 비용이 프로세스보다 저렴합니다.
- 메모리를 공유하기 때문에 프로세스 간 통신 같은 복잡한 절차가 필요 없습니다.
- I/O 바운드 작업에서는 GIL의 영향이 제한적이라 충분히 효과적입니다.
멀티스레딩 단점
- GIL 때문에 순수 CPU 연산에서는 스레드를 늘려도 성능이 거의 개선되지 않으며, 오히려 컨텍스트 스위칭 비용으로 더 느려지는 경우도 있습니다.
- 공유 메모리로 인한 레이스 컨디션 등 동기화 문제를 신경 써야 합니다.
멀티프로세싱 장점
- 각 프로세스가 독립된 GIL을 가지므로 CPU 바운드 작업에서 실제 병렬 처리가 가능합니다.
- 프로세스 간 메모리가 분리되어 있어 레이스 컨디션 같은 문제에서 비교적 자유롭습니다.
멀티프로세싱 단점
- 프로세스 생성 비용이 스레드보다 훨씬 큽니다.
- 프로세스 간 데이터 교환에 직렬화(pickling)가 필요해 통신 비용이 발생하고, 공유 상태 관리가 스레드보다 번거롭습니다.
개인적인 견해로는, "무조건 멀티프로세싱이 빠르다"는 식의 단순화된 설명은 주의가 필요하다고 생각합니다. 작업 하나하나가 매우 가벼운데 프로세스를 대량으로 생성하면, 프로세스 생성·통신 오버헤드가 실제 연산 시간을 초과해 버리는 경우도 흔합니다. 실무에서는 반드시 실제 워크로드를 대상으로 프로파일링을 해본 뒤 스레드/프로세스/비동기 중 어떤 방식이 더 유리한지 확인하는 절차가 필요하다고 봅니다.
4. 추천대상
- 대량의 네트워크 요청, 파일 I/O 처리처럼 대기 시간이 긴 작업이 많다면 threading 또는 asyncio를 우선 고려하는 것을 추천합니다.
- 이미지·영상 처리, 대규모 데이터 연산, 머신러닝 전처리처럼 CPU 연산이 병목인 작업이라면 multiprocessing이 적합합니다.
- I/O와 CPU 작업이 섞여 있는 복잡한 파이프라인이라면, asyncio로 I/O를 처리하고 CPU 집약적 부분만 별도 프로세스로 넘기는 하이브리드 구조도 고려해볼 만합니다.
5. FAQ
Q. GIL은 왜 아직도 존재하나요?
A. GIL은 CPython 내부의 메모리 관리(참조 카운팅)를 스레드 안전하게 만들기 위한 장치입니다. 이를 완전히 제거하려는 시도(PEP 703 등)가 진행 중이지만, 단일 스레드 성능 저하 등 트레이드오프가 있어 아직은 선택적(실험적) 기능으로만 제공되고 있습니다.
Q. I/O 바운드 작업에도 멀티프로세싱을 쓸 수 있나요?
A. 기술적으로는 가능하지만, 프로세스 생성 비용이 크기 때문에 대부분의 경우 threading이나 asyncio가 더 효율적입니다.
Q. 스레드 수를 무한정 늘리면 성능이 계속 좋아지나요?
A. 아닙니다. 스레드가 지나치게 많아지면 컨텍스트 스위칭 오버헤드가 커져 오히려 성능이 떨어질 수 있습니다. 일반적으로 실무 가이드에서는 수백 개 이내로 관리하는 것이 권장됩니다.
Q. asyncio와 멀티프로세싱을 같이 쓸 수 있나요?
A. 예, 가능합니다. asyncio의 run_in_executor()나 ProcessPoolExecutor를 조합해 I/O는 비동기로, CPU 연산은 별도 프로세스로 분리하는 하이브리드 패턴이 실무에서 자주 쓰입니다.
6. 출처
- Medium(Sizan Mahmud), "Python Concurrency Showdown: AsyncIO vs Threading vs Multiprocessing — Which Should You Choose in 2026?"
- velog(jaebig), "python 동시성 관리 (2) - GIL(Global Interpreter Lock)"
- Medium(backendbyeli), "The GIL Is Still Here — Why That Matters in 2026"
- Shane Chang, "Python's GIL and Asyncio: Understanding the Relationship"