Process(進程)

Process 是 OS 分配資源的最小單位——每個 Process 有自己的記憶體空間、文件描述符、資源。

Process A            Process B
┌──────────────┐    ┌──────────────┐
│ Memory Space │    │ Memory Space │  ← 隔離的
│ File handles │    │ File handles │
│ Stack        │    │ Stack        │
└──────────────┘    └──────────────┘

優點:完全隔離,一個 Process crash 不影響其他。

缺點:建立 Process 的成本高(fork 需要複製記憶體);Process 之間通信(IPC)複雜(管道、socket、共享記憶體)。

適用:需要強隔離的場景(Docker container 就是 process 隔離);CPU 密集型任務利用多核(Python 用 multiprocessing 繞過 GIL)。


Thread(線程)

Thread 是同一個 Process 裡的執行單位,共享 Process 的記憶體空間。

Process
┌──────────────────────────────────┐
│ Memory Space(共享)               │
│ File handles(共享)               │
│  Thread 1: Stack + Program Counter │
│  Thread 2: Stack + Program Counter │
└──────────────────────────────────┘

優點:比 Process 輕量(不需要複製記憶體);共享記憶體讓 Thread 間通信容易。

缺點:共享記憶體帶來 race condition 和 deadlock 的風險;需要鎖機制(mutex、semaphore)。

Context Switch 成本:OS 切換 Thread 時需要保存和恢復 CPU 暫存器、stack。一個 Thread 大約 1MB stack,一個 process 裡幾千個 thread 就會有記憶體壓力和大量 context switch 開銷。


Coroutine(協程)

Coroutine 是在用戶空間(不是 OS kernel)管理的「輕量 thread」,由程式(而不是 OS)決定什麼時候切換。

OS Thread 1
└── Event Loop
    ├── Coroutine A: running...
    │   await io_operation()  → 讓出執行權
    │   
    ├── Coroutine B: running...  ← Event loop 接手
    │   await io_operation()  → 讓出執行權
    │
    └── Coroutine A: resumed  ← I/O 完成,繼續

優點

  • 成本極低(Go 的 goroutine 初始只需 ~2KB)
  • 同一個 OS thread 可以跑幾萬甚至幾十萬個 coroutine
  • 沒有真正的並行(預設單 thread),所以沒有 race condition(對 I/O 密集型)

缺點

  • 需要 async 相容的 I/O 庫(同步的 I/O 會阻塞整個 event loop)
  • CPU 密集型任務仍需要多 thread 或多 process

三者的選擇

ProcessThreadCoroutine
隔離性
記憶體開銷中(~1MB/thread)低(~2KB/goroutine)
建立成本
通信IPC(複雜)共享記憶體(有 race 風險)await/channel
適合CPU 密集、強隔離CPU 密集 + 共享狀態I/O 密集、高並發

Go 的 goroutine 是 coroutine 的實作,但 Go runtime 會把 goroutine 分配到多個 OS thread——同時有 coroutine 的輕量和多核的 CPU 利用率。

Node.js / Python asyncio 是 coroutine 但只用單 OS thread——適合 I/O 密集,CPU 密集需要 worker_threads / multiprocessing。

Java virtual thread(JDK 21):JVM 層的 coroutine 實作,讓你用同步的語法寫,底層自動轉為非阻塞。