이코에코(Eco²) Knowledge Base/Python

동시성 모델과 Green Thread

mango_fr 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}")  # 400000

3.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 관련


변경 이력

날짜 내용
2025-12-24 최초 작성
2025-12-24 Green Thread 개념 추가 (M:N 모델, 다언어 비교)