위 페이지에서 지속적으로 언급했던 것처럼, CPython 기준으로 1개의 스레드에서 1개의 작업만 이뤄짐
이러한 특징 때문에 멀티스레드나 멀티프로세스를 사용하는 방법이 존재
한 프로세스 안에 스레드를 여러 개 만드는 방식
핵심은 GIL 때문에 파이썬 코드를 실행하는 스레드는 항상 한 번에 1개라는 점
다만 스레드가 I/O 대기(네트워크 응답, 디스크 읽기 등)에 들어가면 그 순간 GIL를 놓아줌
이러한 특정 때문에 스레드를 여러 개 만들어 놓으면,
스레드1이 요청을 보내고 응답을 기다림 → GIL을 놓음 스레드2가 그 GIL을 잡아서 자기 요청을 전송 후 응답을 기다림 → GIL 놓음 스레드3이 그 GIL을 잡아서……
이와 같이 여러 스레드의 대기 시간이 서로 겹치게 되어서, 처리 속도가 더 빨라지게 됌
예시:
import time
from concurrent.futures import **ThreadPoolExecutor**
def fetch(n):
time.sleep(1) # 1초 응답 대기 (이때 GIL을 놓음)
return n
# 순차: 5초 / 스레드풀: 약 1초
with ThreadPoolExecutor(max_workers=5) as ex:
results = list(ex.map(fetch, range(5)))
주의점
프로세스 자체를 여러 개 띄우는 방식
각 프로세스가 독립된 인터프리터와 독립된 인터페이스와 독립된 GIL을 가져, CPU 코어에서 진짜로 동시에 실행
from concurrent.futures import **ProcessPoolExecutor**
def cpu_work(n):
return sum(i * i for i in range(n)) # 순수 계산
if __name__ == "__main__": # 가드 필수
with ProcessPoolExecutor(max_workers=4) as ex:
results = list(ex.map(cpu_work, [10_000_000] * 4))