GIL(Global Interpreter Lock, 전역 인터프리터 락)은 CPython(표준 Python 구현)의 뮤텍스로, 한 번에 하나의 스레드만 Python 바이트코드를 실행하게 허용합니다. 이는 Python 스레드가 여러 CPU 코어에서 진정으로 병렬로 Python 코드를 실행할 수 없음을 의미합니다 — 동시성의 주요 고려 사항입니다.
GIL이 실제로 의미하는 것
멀티코어 CPU에서 여러 스레드가 있더라도:
어느 순간에든 오직 하나의 스레드만 Python 바이트코드를 실행함.
→ CPython에서 스레딩은 CPU 바운드 병렬성을 제공하지 않음.
핵심 구분: CPU 바운드 vs I/O 바운드
import threading
# ❌ 스레드로 CPU 바운드 작업 — 속도 향상 없음 (GIL이 직렬화)
def cpu_task():
sum(i * i for i in range(10_000_000)) # 무거운 계산
# 이것을 하는 4개의 스레드는 본질적으로 1개만큼 느리게 실행됨 (GIL 경합)
# ✅ 스레드로 I/O 바운드 작업 — 실질적인 이점
def io_task():
response = requests.get(url) # 네트워크 대기 — 대기 중 GIL을 해제함
# 한 스레드가 I/O를 기다리는 동안 GIL이 다른 스레드를 위해 해제됨 → 동시성이 동작
결정적인 뉘앙스: GIL은 I/O 중(네트워크, 파일, sleep)과 많은 C 확장 내부에서 해제됩니다. 따라서 스레드는 I/O 바운드 작업(여러 대기가 겹침)에는 도움이 되지만, CPU 바운드 작업(GIL이 계산을 직렬화)에는 그렇지 않습니다.
CPU 바운드 작업을 위한 GIL 우회
from multiprocessing import Pool
# ✅ multiprocessing은 각자 자체 GIL을 가진 별도의 프로세스를 사용 → 진정한 병렬성
with Pool(4) as p:
results = p.map(cpu_task, data) # 4개 코어에서 진정으로 병렬 실행
CPU 바운드 병렬성을 위해서는 multiprocessing(별도 프로세스가 단일 GIL을 우회)을 사용하거나, 무거운 작업이 GIL을 해제한 채 C에서 실행되는 라이브러리(NumPy 등)를 사용하세요.
의사결정 가이드
I/O 바운드 (네트워크, 파일, DB) → threading 또는 asyncio (I/O 시 GIL 해제)
CPU 바운드 (계산) → multiprocessing (또는 C 확장 라이브러리)
미래
Python 3.13+는 실험적인 free-threaded(no-GIL) 빌드를 도입했고,
GIL의 영향을 줄이는 작업이 계속되고 있습니다 — 하지만 여전히 기본 현실입니다.
왜 중요한가
GIL은 Python의 가장 중요하면서 — 가장 오해받는 — 특성 중 하나입니다.
CPU 집약적 작업에 스레드를 사용하면 왜 속도 향상이 없는지(흔한 실수), 그리고 올바른 동시성 도구가 왜 작업 부하에 따라 달라지는지를 설명합니다: I/O 바운드에는 threading/asyncio(대기 중 GIL 해제), CPU 바운드에는 multiprocessing(진정한 병렬성을 위한 별도 프로세스).
GIL을 오해하면 확장되지 않는 비효과적인 동시성 코드로 이어집니다.
이를 아는 것 — 그리고 그것이 이끄는 I/O-vs-CPU 결정 — 은 실제로 성능을 내는 동시성 Python을 작성하는 데 필수적입니다.
