Python 3.13 이전까지는 GIL이라는 규약이 해제 불가능했다.
단 한 가지 방법이 있다. 멀티스레딩을 지원하는 언어의 컴파일된 라이브러리를 쓰면 된다. FFI를 참조하라.
그러나 3.13 이후부터 free-threaded
JIT를 지원하기 시작했다.
GIL, Global Interpretrior Lock이란, 하나의 인터프리터는 무조건 하나의 Thread가 실제 실행되며, 멀티스레딩으로 표현되는 API는 실제로는 시분할로 작동한다는 사실을 의미한다.
(컴퓨터공학중 운영체제에서) 시분할이라고 함은, 시간을 기준으로 Task를 나눠서 하나의 처리자로 여러 태스크에 컴퓨팅 파워를 쏟아붇는 과정등을 이야기한다.
따라서, free-threaded
가 적용되지 않은 경우라면, 기존 multi-Thread의 문제가 재현되지 않을 것이다.
(임계영역 문제가 재현되지 않는 이유도 GIL때문이다.)
대부분의 웹 문제는 통신에서 시간을 낭비하는 경우가 많기 때문이다.
구분 | AsyncIO | 멀티스레딩 (Multithreading) | 멀티프로세싱 (Multiprocessing) |
---|---|---|---|
핵심 개념 | 단일 스레드, 이벤트 루프 기반의 비동기 처리 | 한 프로세스 내에서 여러 스레드가 실행 흐름 공유 | 여러 프로세스가 독립적으로 실행 |
실행 단위 | 태스크 (Task) | 태스크를 처리하는 단위,스레드 (Thread) | 프로세스 (Process) |
메모리 공유 | ✅ 단일 스레드 내에서 메모리 공유 | ✅ 같은 프로세스 내 스레드 간 메모리 공유 | ❌ 독립된 메모리 공간 (IPC 필요) |
GIL의 영향 | ⚠️ 받지 않음 (단일 스레드이므로) | ⛔️ 받음 (동시에 한 스레드만 Python 코드 실행) | ✅ 받지 않음 (프로세스별 GIL) |
주요 장점 | 매우 가볍고, 문맥 교환 비용이 거의 없음 | 메모리 공유가 쉬워 데이터 교환이 간단 | 진정한 의미의 병렬 처리 가능 |
주요 단점 | CPU 집약적 작업에 부적합 | GIL 때문에 CPU 집약적 작업에서 성능 향상 제한적 | 메모리 사용량이 크고, 프로세스 생성/통신 비용이 높음 |
적합한 작업 | I/O 바운드 (네트워크 통신, 파일 입출력 등) | I/O 바운드 (단, GIL 우회 가능한 C 확장 라이브러리 사용 시 CPU 바운드도 일부 가능) | CPU 바운드 (복잡한 연산, 데이터 분석, 비디오 인코딩 등) |
import asyncio
import time
import aiohttp # 비동기 HTTP 요청을 위한 라이브러리 (pip install aiohttp)
# I/O 바운드 작업을 시뮬레이션하는 비동기 함수 (Coroutine)
async def fetch_url(session, url):
"""지정된 URL에서 데이터를 비동기적으로 가져옵니다."""
print(f"요청 시작: {url}")
try:
# 'await'를 통해 네트워크 응답을 기다리는 동안 이벤트 루프는 다른 작업을 처리함
async with session.get(url) as response:
data = await response.text()
print(f"요청 완료: {url}, 길이: {len(data)} bytes")
return f"{url} 로드 성공"
except Exception as e:
print(f"오류 발생: {url}, {e}")
return f"{url} 로드 실패"
async def main():
"""메인 실행 함수"""
urls_to_fetch = [
"https://www.google.com",
"https://www.naver.com",
"https://www.python.org",
"https://github.com",
"https://news.ycombinator.com",
]
start_time = time.time()
async with aiohttp.ClientSession() as session:
# 각 URL에 대한 비동기 작업을 생성
tasks = [fetch_url(session, url) for url in urls_to_fetch]
# asyncio.gather를 사용하여 모든 작업이 완료될 때까지 동시에 실행
results = await asyncio.gather(*tasks)
print("\n--- 모든 작업 완료 ---")
print(results)
end_time = time.time()
print(f"\n총 실행 시간: {end_time - start_time:.2f} 초")
if __name__ == "__main__":
# 이벤트 루프를 시작하여 main 코루틴을 실행
asyncio.run(main())