-
동시성 모델과 Green ThreadPython 2025. 12. 24. 21:06

이 문서는 Python에서 사용 가능한 4가지 동시성 모델을 비교한다.
먼저 동시성을 보다 잘 이해하기 위해 혼동되는 개념인 병렬성과의 차이를 짚고 가자.┌─────────────────────────────────────────────────────────────┐ │ 동시성 vs 병렬성 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Concurrency (동시성): │ │ ───────────────────── │ │ "여러 작업을 번갈아 가며 처리하는 것" │ │ • 한 번에 하나씩만 실행 │ │ • 빠르게 전환하여 동시에 실행되는 것처럼 보임 │ │ • 예: 싱글 코어에서 멀티태스킹 │ │ │ │ Task A: ██░░░░██░░░░██ │ │ Task B: ░░██░░░░██░░░░██ │ │ ─────────────────────────▶ time │ │ (번갈아 실행) │ │ │ │ ───────────────────────────────────────────────────────── │ │ │ │ Parallelism (병렬성): │ │ ───────────────────── │ │ "여러 작업을 동시에 실행하는 것" │ │ • 실제로 동시에 실행 │ │ • 멀티코어 필수 │ │ • 예: 멀티코어에서 각 코어가 별도 작업 │ │ │ │ Core 1: ████████████████████ │ │ Core 2: ████████████████████ │ │ ─────────────────────────▶ time │ │ (실제 동시 실행) │ │ │ └─────────────────────────────────────────────────────────────┘
1. 4가지 동시성 모델
Python에서 사용 가능한 동시성 모델:
모델 단위 특징 사용 사례 Process 프로세스 완전 격리, GIL 우회 CPU-bound Thread OS 스레드 공유 메모리, GIL 영향 I/O-bound (제한적) Greenlet 유저 스레드 Monkey patching, 협력적 I/O-bound (gevent) Coroutine 코루틴 async/await, Event Loop I/O-bound (asyncio) ┌─────────────────────────────────────────────────────────────┐ │ 동시성 모델 스펙트럼 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 무거움 가벼움 │ │ (Heavyweight) (Lightweight)│ │ ←─────────────────────────────────────────────────────────→│ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Process │ │ Thread │ │ Greenlet │ │ Coroutine│ │ │ │ (fork) │ │ (OS) │ │ (user) │ │(async/ │ │ │ │ │ │ │ │ │ │ await) │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ 특성: │ │ • 메모리 격리 • GIL 영향 • Monkey • Event Loop │ │ • IPC 필요 • Race Patching 필요 │ │ • ~MB 메모리 Condition • 협력적 • 언어 지원 │ │ • ~MB 메모리 멀티태스킹 • ~KB 메모리 │ │ • ~KB 메모리 │ │ │ └─────────────────────────────────────────────────────────────┘
2. Process (프로세스)
2.1 개념
프로세스는 운영체제가 관리하는 독립적인 실행 단위이다.
각 프로세스는 별도의 메모리 공간, 파일 디스크립터, 환경변수를 가진다.┌─────────────────────────────────────────────────────────────┐ │ Process 구조 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Process 1 │ │ Process 2 │ │ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ │ │ Code │ │ │ │ Code │ │ │ │ │ ├───────────┤ │ │ ├───────────┤ │ │ │ │ │ Data │ │ │ │ Data │ │ │ │ │ ├───────────┤ │ │ ├───────────┤ │ │ │ │ │ Heap │ │ │ │ Heap │ │ │ │ │ ├───────────┤ │ │ ├───────────┤ │ │ │ │ │ Stack │ │ │ │ Stack │ │ │ │ │ └───────────┘ │ │ └───────────┘ │ │ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ │ │ GIL 1 │ │ │ │ GIL 2 │ │ │ │ │ └───────────┘ │ │ └───────────┘ │ │ │ └─────────────────┘ └─────────────────┘ │ │ ↑ ↑ │ │ │ OS Scheduler │ │ │ └──────────┬───────────┘ │ │ ▼ │ │ ┌──────────────┐ │ │ │ CPU Core(s) │ │ │ └──────────────┘ │ │ │ │ 특징: │ │ • 완전히 격리된 메모리 공간 │ │ • 프로세스별 독립 GIL → CPU-bound 작업에 적합 │ │ • IPC (Inter-Process Communication) 필요 │ │ • fork() 시 메모리 복사 → 오버헤드 큼 │ │ │ └─────────────────────────────────────────────────────────────┘2.2 Python 예제
from multiprocessing import Process, Queue def worker(q, n): """CPU-intensive 작업""" result = sum(i * i for i in range(n)) q.put(result) if __name__ == "__main__": q = Queue() processes = [] for i in range(4): # 4개 프로세스 생성 p = Process(target=worker, args=(q, 1000000)) p.start() processes.append(p) for p in processes: p.join() results = [q.get() for _ in range(4)]2.3 장단점
장점 단점 GIL 우회 (진정한 병렬성) 메모리 오버헤드 (~MB/process) 메모리 격리 (안전) IPC 필요 (복잡성) 크래시 격리 fork() 비용 CPU-bound에 최적 Windows에서 제한적
3. Thread (스레드)
3.1 개념
스레드는 프로세스 내에서 실행되는 경량 실행 단위이다.
같은 프로세스의 스레드들은 메모리를 공유한다.┌─────────────────────────────────────────────────────────────┐ │ Thread 구조 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────┐│ │ │ Process ││ │ │ ┌──────────────────────────────────────────────────┐ ││ │ │ │ 공유 메모리 │ ││ │ │ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ ││ │ │ │ │ Code │ │ Data │ │ Heap │ │ ││ │ │ │ └───────────┘ └───────────┘ └───────────┘ │ ││ │ │ └──────────────────────────────────────────────────┘ ││ │ │ ││ │ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ││ │ │ │ Thread 1 │ │ Thread 2 │ │ Thread 3 │ ││ │ │ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ ││ │ │ │ │ Stack │ │ │ │ Stack │ │ │ │ Stack │ │ ││ │ │ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ ││ │ │ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ ││ │ │ │ │ │ ││ │ │ └───────────────┼───────────────┘ ││ │ │ │ ││ │ │ ┌─────▼─────┐ ││ │ │ │ GIL │ ← 하나만 실행 가능! ││ │ │ └───────────┘ ││ │ └────────────────────────────────────────────────────────┘│ │ │ │ Python GIL 영향: │ │ • 한 번에 하나의 스레드만 Python 바이트코드 실행 │ │ • CPU-bound에서는 병렬성 없음 │ │ • I/O-bound에서는 I/O 대기 시 GIL 해제 │ │ │ └─────────────────────────────────────────────────────────────┘3.2 Python 예제
import threading import time counter = 0 lock = threading.Lock() def worker(): global counter for _ in range(100000): with lock: # Race condition 방지 counter += 1 threads = [threading.Thread(target=worker) for _ in range(4)] for t in threads: t.start() for t in threads: t.join() print(f"Counter: {counter}") # 4000003.3 GIL과 스레드
┌─────────────────────────────────────────────────────────────┐ │ GIL이 Thread에 미치는 영향 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ CPU-bound 작업: │ │ ─────────────── │ │ Thread 1: ████....████....████ │ │ Thread 2: ....████....████....████ │ │ Thread 3: ........████........████ │ │ │ │ │ │ │ │ GIL GIL GIL GIL │ │ 전환 전환 전환 전환 │ │ │ │ → 실제 병렬 실행 없음, 전환 오버헤드만 발생 │ │ → 싱글 스레드보다 더 느릴 수 있음! │ │ │ │ ───────────────────────────────────────────────────────── │ │ │ │ I/O-bound 작업: │ │ ────────────── │ │ Thread 1: ██[I/O 대기........]██[I/O 대기......]██ │ │ Thread 2: ██[I/O 대기....]██[I/O 대기........]██ │ │ Thread 3: ██[I/O 대기......]██[I/O 대기....]██ │ │ │ │ │ │ I/O 시작 시 I/O 완료 시 │ │ GIL 해제 GIL 재획득 │ │ │ │ → I/O 대기 중 다른 스레드 실행 가능 │ │ → 동시성 효과 있음 │ │ │ └─────────────────────────────────────────────────────────────┘3.4 장단점
장점 단점 메모리 공유 (빠른 통신) GIL로 인한 CPU-bound 제한 프로세스보다 경량 Race condition 위험 I/O-bound에서 효과적 디버깅 어려움 OS 레벨 스케줄링 컨텍스트 스위치 비용
4. Green Thread와 Greenlet
4.1 Green Thread란?
Green Thread는 OS 커널이 아닌 런타임/VM/라이브러리가 관리하는 경량 스레드이다.
"Green"이라는 이름은 Sun Microsystems의 Green Team에서 유래했다 (Java 1.0의 초기 스레드 모델).┌─────────────────────────────────────────────────────────────┐ │ Green Thread vs OS Thread │ ├─────────────────────────────────────────────────────────────┤ │ │ │ OS Thread (Native Thread): │ │ ───────────────────────── │ │ • 커널이 스케줄링 │ │ • 커널 모드 전환 필요 (비용 높음) │ │ • OS가 선점적(preemptive) 관리 │ │ • 스레드당 ~MB 스택 메모리 │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Kernel Space │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ │ │Thread 1 │ │Thread 2 │ │Thread 3 │ │ │ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ │ │ │ │ └────────────┼────────────┘ │ │ │ │ ▼ │ │ │ │ OS Scheduler │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ ───────────────────────────────────────────────────────── │ │ │ │ Green Thread: │ │ ───────────── │ │ • 런타임/라이브러리가 스케줄링 │ │ • 유저 모드에서 전환 (비용 낮음) │ │ • 협력적(cooperative) 또는 선점적 관리 │ │ • 스레드당 ~KB 스택 메모리 │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ User Space │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ Runtime / VM / Library │ │ │ │ │ │ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │ │ │ │ │ │ │Green 1│ │Green 2│ │Green 3│ │Green N│ ... │ │ │ │ │ │ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ │ │ │ │ │ │ └─────────┼─────────┼─────────┘ │ │ │ │ │ │ ▼ ▼ │ │ │ │ │ │ User-space Scheduler │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ 1-N OS Threads │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘4.2 다양한 언어의 Green Thread 구현
언어/런타임 구현체 M:N 모델 스케줄링 특징 Java 1.0 Green Threads M:1 협력적 최초, 현재는 Native Erlang/OTP Process M:N 선점적 Actor 모델, 수백만 개 Go Goroutine M:N 선점적 (1.14+) 경량, 채널 통신 Rust async/await + Tokio M:N 협력적 Zero-cost abstraction Python/Gevent Greenlet M:1 협력적 Monkey patching Python/asyncio Coroutine M:1 협력적 언어 지원 Kotlin Coroutine M:N 협력적 Structured concurrency Lua Coroutine M:1 협력적 단순, 경량 4.3 M:N 스레딩 모델
┌─────────────────────────────────────────────────────────────┐ │ M:N 스레딩 모델 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1:1 모델 (Native Thread): │ │ ───────────────────────── │ │ • 1 User Thread = 1 OS Thread │ │ • Python threading, Java (현재) │ │ │ │ User: [T1] [T2] [T3] │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ OS: [OS1] [OS2] [OS3] │ │ │ │ ───────────────────────────────────────────────────────── │ │ │ │ M:1 모델 (Pure Green Thread): │ │ ───────────────────────────── │ │ • M Green Threads : 1 OS Thread │ │ • Python asyncio, Gevent (단일 스레드) │ │ │ │ User: [G1] [G2] [G3] [G4] [G5] [G6] │ │ │ │ │ │ │ │ │ │ └────┴────┴────┴────┴────┘ │ │ ▼ │ │ OS: [OS1] │ │ │ │ ───────────────────────────────────────────────────────── │ │ │ │ M:N 모델 (Hybrid): │ │ ───────────────── │ │ • M Green Threads : N OS Threads (M >> N) │ │ • Go goroutine, Erlang process, Java Virtual Threads │ │ │ │ User: [G1][G2][G3][G4][G5][G6][G7][G8][G9][G10]... │ │ │ │ │ │ │ │ │ │ │ │ │ │ └───┴───┴───┼───┴───┴───┼───┴───┴───┘ │ │ ▼ ▼ │ │ OS: [OS1] [OS2] [OS3] │ │ │ │ 장점: │ │ • Green Thread의 경량성 + OS Thread의 병렬성 │ │ • 멀티코어 활용 가능 │ │ • 블로킹 I/O 시에도 다른 OS Thread가 계속 실행 │ │ │ └─────────────────────────────────────────────────────────────┘4.4 Greenlet: Python의 Green Thread 구현
Greenlet은 Python에서 Green Thread를 구현한 라이브러리이다.
gevent, eventlet 등이 Greenlet을 기반으로 구축되었다.┌─────────────────────────────────────────────────────────────┐ │ Greenlet 구조 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────┐│ │ │ Process ││ │ │ ┌──────────────────────────────────────────────────┐ ││ │ │ │ Main Thread │ ││ │ │ │ │ ││ │ │ │ ┌────────────────────────────────────────────┐ │ ││ │ │ │ │ Gevent Hub │ │ ││ │ │ │ │ (메인 Event Loop greenlet) │ │ ││ │ │ │ └─────────────────────┬──────────────────────┘ │ ││ │ │ │ │ │ ││ │ │ │ ┌──────────────────┼──────────────────┐ │ ││ │ │ │ │ │ │ │ ││ │ │ │ ▼ ▼ ▼ │ ││ │ │ │ ┌────────┐ ┌────────┐ ┌────────┐ │ ││ │ │ │ │greenlet│ │greenlet│ │greenlet│ │ ││ │ │ │ │ 1 │◄────▶│ 2 │◄────▶│ 3 │ │ ││ │ │ │ │ (task) │ │ (task) │ │ (task) │ │ ││ │ │ │ └────────┘ └────────┘ └────────┘ │ ││ │ │ │ │ │ │ │ ││ │ │ │ └────────────────┼────────────────┘ │ ││ │ │ │ ▼ │ ││ │ │ │ 협력적 전환 │ ││ │ │ │ (switch() 호출 시) │ ││ │ │ └──────────────────────────────────────────────────┘ ││ │ └────────────────────────────────────────────────────────┘│ │ │ │ 특징: │ │ • OS 스레드가 아닌 유저 레벨 구현 (Green Thread) │ │ • 협력적 멀티태스킹 (자발적 전환) │ │ • Monkey patching으로 기존 코드 자동 변환 │ │ • 매우 경량 (~KB/greenlet) │ │ • M:1 모델 (단일 OS 스레드에서 실행) │ │ │ └─────────────────────────────────────────────────────────────┘4.5 협력적 멀티태스킹 vs 선점적 멀티태스킹
┌─────────────────────────────────────────────────────────────┐ │ 협력적 vs 선점적 멀티태스킹 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 선점적 (Preemptive) - Thread: │ │ ───────────────────────────── │ │ • OS가 강제로 실행 중인 스레드를 중단 │ │ • 스레드는 언제든 중단될 수 있음 │ │ • 예측 불가능한 전환 시점 │ │ │ │ Thread A: ███████|──────────────|███████ │ │ 실행 OS가 강제 중단 재개 │ │ │ │ ───────────────────────────────────────────────────────── │ │ │ │ 협력적 (Cooperative) - Greenlet: │ │ ──────────────────────────────── │ │ • greenlet이 자발적으로 제어권 양보 │ │ • I/O 또는 명시적 switch() 시에만 전환 │ │ • 예측 가능한 전환 시점 │ │ │ │ Greenlet A: ███████████████|──────|████████████████ │ │ 실행 (중단 안됨) I/O 다시 실행 │ │ 대기 │ │ │ │ 장점: Race condition 감소, 예측 가능 │ │ 단점: CPU-bound가 다른 greenlet 블로킹 가능 │ │ │ └─────────────────────────────────────────────────────────────┘4.6 Python 예제 (Gevent)
import gevent from gevent import monkey monkey.patch_all() # 표준 라이브러리 패치 import requests # 이제 비동기처럼 동작 def fetch(url): response = requests.get(url) # 자동으로 greenlet 전환 return response.status_code # 동시에 여러 요청 urls = [ "https://api.github.com", "https://api.twitter.com", "https://api.facebook.com", ] # gevent.spawn()으로 greenlet 생성 greenlets = [gevent.spawn(fetch, url) for url in urls] gevent.joinall(greenlets) results = [g.value for g in greenlets]4.7 Monkey Patching 상세
┌─────────────────────────────────────────────────────────────┐ │ Monkey Patching 동작 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ monkey.patch_all() 호출 시: │ │ │ │ 표준 라이브러리 → Gevent 버전 │ │ ───────────────────────────────────────────── │ │ socket.socket → gevent.socket │ │ ssl.SSLSocket → gevent.ssl │ │ time.sleep → gevent.sleep │ │ select.select → gevent.select │ │ threading.Thread → gevent._threading │ │ os.fork → gevent.os.fork │ │ subprocess.Popen → gevent.subprocess │ │ │ │ ───────────────────────────────────────────────────────── │ │ │ │ 코드 변경 없이 비동기 동작: │ │ │ │ # 원본 코드 (블로킹) │ │ import socket │ │ sock = socket.socket() │ │ sock.connect(("example.com", 80)) │ │ sock.recv(1024) # 블로킹! │ │ │ │ # monkey.patch_all() 후 │ │ # 같은 코드가 자동으로: │ │ # 1. libev에 I/O watcher 등록 │ │ # 2. hub.switch()로 다른 greenlet 실행 │ │ # 3. I/O 완료 시 다시 전환 │ │ │ └─────────────────────────────────────────────────────────────┘4.8 Green Thread / Greenlet 장단점
장점 단점 매우 경량 (~KB) CPU-bound 블로킹 위험 기존 동기 코드 재사용 Monkey patch 부작용 가능 수천 개 동시 실행 일부 C 확장과 호환성 문제 협력적 → Race condition 감소 디버깅 어려움
5. Coroutine (코루틴)
5.1 개념
코루틴은 언어 레벨에서 지원하는 동시성 모델이다.
Python에서는async/await문법으로 구현된다.┌─────────────────────────────────────────────────────────────┐ │ Coroutine 구조 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌────────────────────────────────────────────────────────┐│ │ │ Process ││ │ │ ┌──────────────────────────────────────────────────┐ ││ │ │ │ Main Thread │ ││ │ │ │ │ ││ │ │ │ ┌────────────────────────────────────────────┐ │ ││ │ │ │ │ asyncio Event Loop │ │ ││ │ │ │ │ │ │ ││ │ │ │ │ ┌──────────────────────────────────────┐ │ │ ││ │ │ │ │ │ Ready Queue │ │ │ ││ │ │ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ ││ │ │ │ │ │ │Task 1│ │Task 2│ │Task 3│ ... │ │ │ ││ │ │ │ │ │ │(coro)│ │(coro)│ │(coro)│ │ │ │ ││ │ │ │ │ │ └──────┘ └──────┘ └──────┘ │ │ │ ││ │ │ │ │ └──────────────────────────────────────┘ │ │ ││ │ │ │ │ │ │ │ ││ │ │ │ │ ▼ │ │ ││ │ │ │ │ ┌──────────────────────────────────────┐ │ │ ││ │ │ │ │ │ Selector (epoll/kqueue) │ │ │ ││ │ │ │ │ │ I/O 감시 │ │ │ ││ │ │ │ │ └──────────────────────────────────────┘ │ │ ││ │ │ │ └────────────────────────────────────────────┘ │ ││ │ │ └──────────────────────────────────────────────────┘ ││ │ └────────────────────────────────────────────────────────┘│ │ │ │ 특징: │ │ • async/await 문법 (언어 레벨 지원) │ │ • 명시적 전환 지점 (await) │ │ • Event Loop 필수 │ │ • 매우 경량 (~KB/coroutine) │ │ │ └─────────────────────────────────────────────────────────────┘5.2 Coroutine의 상태 전이
┌─────────────────────────────────────────────────────────────┐ │ Coroutine 상태 전이 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────┐ await ┌──────────┐ │ │ │ │──────────────▶│ │ │ │ │ RUNNING │ │ SUSPENDED│ │ │ │ │◀──────────────│ │ │ │ └──────────┘ resume └──────────┘ │ │ │ │ │ │ │ return/raise │ I/O 완료 │ │ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ │ │ │ COMPLETED│ │ READY │ │ │ └──────────┘ └────┬─────┘ │ │ │ │ │ │ 스케줄링 │ │ ▼ │ │ ┌──────────┐ │ │ │ RUNNING │ │ │ └──────────┘ │ │ │ │ 예시: │ │ async def fetch(): │ │ # RUNNING │ │ response = await client.get(url) # → SUSPENDED │ │ # I/O 완료 → READY → RUNNING │ │ return response # → COMPLETED │ │ │ └─────────────────────────────────────────────────────────────┘5.3 Python 예제 (asyncio)
import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: return await response.json() async def main(): async with aiohttp.ClientSession() as session: # 동시에 여러 요청 (gather) tasks = [ fetch(session, "https://api.github.com/users/octocat"), fetch(session, "https://api.github.com/repos/python/cpython"), fetch(session, "https://api.github.com/orgs/python"), ] results = await asyncio.gather(*tasks) return results # Event Loop 실행 results = asyncio.run(main())5.4 await의 의미
┌─────────────────────────────────────────────────────────────┐ │ await의 동작 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ async def my_coroutine(): │ │ print("시작") # 1. 즉시 실행 │ │ await asyncio.sleep(1) # 2. 여기서 제어권 양보 │ │ print("끝") # 3. 1초 후 재개 │ │ │ │ ───────────────────────────────────────────────────────── │ │ │ │ Event Loop 관점: │ │ │ │ 시간 ──────────────────────────────────────────────────▶ │ │ │ │ Coro A: [시작]─await─[대기]────────────────────[끝] │ │ Coro B: ────────────[시작]─await─[대기]────[끝] │ │ Coro C: ─────────────────────────[시작]────────[끝] │ │ │ │ │ │ │ │ │ │ │ t=0 t=1 t=2 t=3 t=4 │ │ │ │ 핵심: await는 "여기서 다른 코루틴 실행해도 됨"을 명시 │ │ │ └─────────────────────────────────────────────────────────────┘5.5 장단점
장점 단점 언어 레벨 지원 (명확한 문법) async/await 전파 (바이러스성) 명시적 전환 지점 기존 동기 코드 수정 필요 매우 경량 (~KB) Event Loop 필수 디버깅 용이 일부 라이브러리 미지원 Type hint 지원 CPU-bound 블로킹 위험
6. 컨텍스트 스위치 비용
6.1 비용 비교
┌─────────────────────────────────────────────────────────────┐ │ Context Switch 비용 비교 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 비용 (높음 → 낮음): │ │ │ │ Process ████████████████████████████████ ~1-10ms │ │ │ │ │ ├── TLB flush │ │ ├── 페이지 테이블 전환 │ │ ├── 캐시 무효화 │ │ └── 커널 모드 전환 │ │ │ │ Thread ████████████████████ ~1-100µs │ │ │ │ │ ├── 레지스터 저장/복원 │ │ ├── 스택 전환 │ │ └── 커널 모드 전환 │ │ │ │ Greenlet ████████ ~1-10µs │ │ │ │ │ ├── 스택 전환만 │ │ └── 유저 모드에서 처리 │ │ │ │ Coroutine ████ ~100ns-1µs │ │ │ │ │ └── 상태 저장만 (힙에 저장) │ │ │ └─────────────────────────────────────────────────────────────┘6.2 메모리 사용량
모델 기본 메모리 10,000개 생성 시 Process ~10-50MB 100GB+ (불가능) Thread ~1-8MB (스택) 10-80GB Greenlet ~4-8KB 40-80MB Coroutine ~2-4KB 20-40MB
7. 사용 시나리오
7.1 선택 가이드
┌─────────────────────────────────────────────────────────────┐ │ 모델 선택 가이드 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 작업 유형 판별 │ │ │ │ │ ┌─────────────┴─────────────┐ │ │ ▼ ▼ │ │ CPU-bound? I/O-bound? │ │ │ │ │ │ ▼ │ │ │ ┌──────────────┐ ┌─────────────┴─────────────┐ │ │ │ Process │ │ │ │ │ │ (multiproc) │ ▼ ▼ │ │ └──────────────┘ 기존 코드 수정 가능? 동기 코드 유지? │ │ │ │ │ │ ┌─────┴─────┐ ┌─────┴─────┐ │ │ ▼ ▼ ▼ ▼ │ │ Yes No Yes No │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Coroutine│ │ Greenlet │ │ Thread │ │ │ │(asyncio) │ │ (gevent) │ │(threading)│ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘7.2 이코에코 적용 사례
컴포넌트 모델 이유 FastAPI (API Layer) Coroutine async/await 네이티브 지원 Celery prefork Process CPU-bound 가능, 독립 GIL Celery gevent Greenlet I/O-bound, 기존 코드 재사용 scan-worker Greenlet OpenAI API I/O, 동기 코드 사용
8. 핵심 요약
┌─────────────────────────────────────────────────────────────┐ │ 핵심 요약 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ 1. Concurrency ≠ Parallelism │ │ ──────────────────────── │ │ • Concurrency: 번갈아 실행 (동시에 하는 척) │ │ • Parallelism: 실제 동시 실행 (멀티코어) │ │ │ │ 2. Green Thread란? │ │ ────────────── │ │ • 커널이 아닌 런타임/라이브러리가 관리하는 경량 스레드 │ │ • Greenlet, Goroutine, Erlang Process 등 │ │ • M:1 또는 M:N 모델로 OS Thread에 매핑 │ │ │ │ 3. 4가지 모델 │ │ ───────────── │ │ • Process: 완전 격리, CPU-bound │ │ • Thread (OS): 공유 메모리, GIL 영향 │ │ • Greenlet (Green Thread): 유저 레벨, Monkey patch │ │ • Coroutine: 언어 지원, async/await │ │ │ │ 4. 선택 기준 │ │ ─────────── │ │ • CPU-bound → Process │ │ • I/O-bound + 새 코드 → Coroutine │ │ • I/O-bound + 기존 코드 → Greenlet (Green Thread) │ │ │ │ 5. 비용 │ │ ────── │ │ • 컨텍스트 스위치: Process > Thread > Greenlet > Coro │ │ • 메모리: Process > Thread > Greenlet ≈ Coroutine │ │ • Green Thread는 유저 모드 전환 → 오버헤드 최소화 │ │ │ └─────────────────────────────────────────────────────────────┘
참고 자료
공식 문서
Green Thread 관련
- Go Goroutines - Go의 Green Thread
- Erlang Processes - Erlang의 Actor 모델
- Java Virtual Threads (JEP 444) - Java 21+ 가상 스레드
- Kotlin Coroutines - Kotlin의 Green Thread
변경 이력
날짜 내용 2025-12-24 최초 작성 2025-12-24 Green Thread 개념 추가 (M:N 모델, 다언어 비교) 'Python' 카테고리의 다른 글
FastAPI Lifespan: 애플리케이션 생명주기 관리 (0) 2026.01.04 FastAPI Clean Example (0) 2025.12.31 arq: AsyncIO-native Task Queue (0) 2025.12.29 Celery: Python 분산 태스크 큐 (0) 2025.12.25 Event Loop: Gevent (0) 2025.12.24