ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 동시성 모델과 Green Thread
    Python 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 모델, 다언어 비교)

    '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

    댓글

ABOUT ME

🎓 부산대학교 정보컴퓨터공학과 학사: 2017.03 - 2023.08
☁️ Rakuten Symphony Jr. Cloud Engineer: 2024.12.09 - 2025.08.31
🏆 2025 AI 새싹톤 우수상 수상: 2025.10.30 - 2025.12.02
🌏 이코에코(Eco²) 백엔드/인프라 고도화 중: 2025.12 - Present

Designed by Mango