目錄
- 三層關係:程式 / 行程 / 執行緒
- 行程(Process)
- 執行緒(Thread)
- 位址空間:共享什麼、不共享什麼
- 為什麼需要執行緒?
- 多執行緒 vs 多行程
- 執行緒生命週期
- 多執行緒帶來的問題
- 常見問題
- 總結
三層關係:程式 / 行程 / 執行緒
這三個詞常被混用,但它們是清楚的三層:
程式 Program → 靜態的可執行檔,躺在磁碟上(指令 + 資料,還沒執行)
行程 Process → 程式被載入記憶體執行的「實例」,擁有獨立資源 = 資源擁有單位
執行緒 Thread → 行程內的執行流,共享行程的位址空間 = CPU 排程單位
比喻:程式是劇本,行程是一場演出(含舞台、道具、燈光),執行緒是台上同時表演的演員。 同一個劇本可以同時開好幾場演出(多個行程),每場演出可以有多個演員(多執行緒)。
| 概念 | 是什麼 | 單位角色 |
|---|---|---|
| Program | 磁碟上的可執行檔 | 靜態,不執行 |
| Process | 執行中的程式實例 | 資源分配 / 擁有單位 |
| Thread | 行程內的執行流 | CPU 排程單位 |
一個行程至少有一條執行緒(主執行緒);可以有多條(多執行緒)。
行程(Process)
行程是「執行中的程式」,作業系統為它配置一整套獨立資源:
- 獨立的位址空間(virtual address space):自己的程式碼段、資料段、堆積(heap)、堆疊(stack)
- 資源:開啟的檔案、網路連線(file descriptors)、環境變數
- 識別:行程 ID(PID)
關鍵:行程之間彼此隔離。A 行程不能直接讀寫 B 行程的記憶體(受 OS 保護)。要溝通得透過 IPC(行程間通訊):管線、訊息佇列、共享記憶體、socket 等。
Process A ─┐ ┌─ Process B
位址空間 A │ 互相隔離,看不到對方 │ 位址空間 B
└── IPC(管線/socket)──┘
執行緒(Thread)
執行緒是行程內部的執行流,是 CPU 真正排程、執行的對象。
- 多條執行緒共享所屬行程的位址空間與資源
- 每條執行緒有自己獨立的堆疊(stack)與暫存器、程式計數器(PC)
- 建立 / 切換執行緒比行程輕量(不必複製整套位址空間)
Process(共享:程式碼、heap、開啟的檔案)
┌──────────┬──────────┬──────────┐
Thread 1 Thread 2 Thread 3
(各自 stack / PC / 暫存器)
因為執行緒共享記憶體,溝通很快(直接讀寫共享變數),但也因此產生競爭問題,需要同步機制(見後文與鎖機制筆記)。
位址空間:共享什麼、不共享什麼
這是理解多執行緒的關鍵:
| 資源 | 行程內多執行緒 | 說明 |
|---|---|---|
| 程式碼段(Code) | ✅ 共享 | 同一份指令 |
| 全域 / 靜態變數、堆積(Heap) | ✅ 共享 | 競爭的來源,需同步 |
| 開啟的檔案 / socket | ✅ 共享 | 共用 file descriptor |
| 堆疊(Stack) | ❌ 各自獨立 | 區域變數、函式呼叫 |
| 暫存器 / 程式計數器(PC) | ❌ 各自獨立 | 各自的執行位置 |
共享 → 溝通快、但要小心 race condition
獨立 → 每條執行緒有自己的呼叫脈絡,互不干擾
為什麼需要執行緒?
1. 並發處理
一條執行緒在等 I/O(讀檔、等網路)時,其他執行緒可以繼續用 CPU,不必整個行程卡住。
2. 善用多核心
多核心 CPU 上,多條執行緒可真正同時在不同核心執行(平行運算),縮短總時間。
3. 反應性(Responsiveness)
GUI / 伺服器把耗時工作丟到背景執行緒,主執行緒保持回應使用者。
4. 比多行程省資源
執行緒共享位址空間,建立、切換成本都比行程低,溝通也不必走 IPC。
多執行緒 vs 多行程
兩種都能並發,取捨不同:
| 面向 | 多執行緒(一個行程內) | 多行程 |
|---|---|---|
| 記憶體 | 共享位址空間 | 各自獨立 |
| 溝通 | 直接讀寫共享變數(快) | IPC(較慢) |
| 建立 / 切換成本 | 低 | 高 |
| 隔離性 / 穩定性 | 低(一條崩潰可能拖垮整個行程) | 高(行程互相隔離) |
| 適用 | 高頻溝通、共享大量資料 | 需要強隔離 / 容錯 |
例:Chrome 用多行程隔離分頁(一個分頁崩潰不影響其他);多數伺服器用多執行緒處理大量並發連線。Python 因 GIL,CPU 密集常改用多行程(multiprocessing)。
執行緒生命週期
從作業系統角度,執行緒在幾個狀態間轉換(各 OS / 語言名稱略異,概念一致):
建立 取得 CPU
新建 New ──► 就緒 Ready ◄──────────► 執行 Running ──► 終止 Terminated
▲ │
│ 事件完成 / 喚醒 │ 等待 I/O / 鎖 / sleep
└────── 阻塞 Blocked ◄───────┘
| 狀態 | 說明 |
|---|---|
| New(新建) | 已建立但尚未開始 |
| Ready(就緒) | 可執行,排隊等 CPU |
| Running(執行) | 正在 CPU 上執行 |
| Blocked / Waiting(阻塞) | 等 I/O、鎖、事件,暫時不參與排程 |
| Terminated(終止) | 執行完畢 |
- Ready → Running:由 CPU 排程器決定(見 CPU 排程筆記)
- Running → Blocked:主動等待(I/O、
sleep、搶鎖失敗) - Running → Ready:時間片用完被搶佔
多執行緒帶來的問題
共享記憶體是雙面刃。多執行緒最核心的風險:
- 競爭條件(Race Condition):多條執行緒同時讀寫共享資料,結果取決於執行順序,不可預期
- 資料不一致 / 更新遺失(Lost Update):一條的修改被另一條覆蓋
- 死鎖(Deadlock):互相等待對方持有的鎖
- 可見性問題:一條執行緒的修改,另一條不一定立刻看到(CPU 快取 / 重排序)
解法是同步機制(鎖、原子操作、號誌等):
共享資料 + 多執行緒 → 需要同步 → 鎖 / atomic / 號誌
同步機制的細節見 Lock 鎖機制完整指南;各語言實作見 Java 多執行緒、Go Goroutines。
常見問題
問題 1:程式和行程差在哪?
程式是磁碟上靜態的可執行檔;行程是程式被載入記憶體執行中的實例。同一個程式可以同時跑出多個行程。
問題 2:執行緒是「作業系統排程的最小單位」嗎?
大致是。現代 OS 中執行緒是 CPU 排程的基本單位,行程是資源擁有單位。但有兩個註解:Linux 實際排的是 "task"(不嚴格區分行程 / 執行緒);而語言層的協程 / goroutine 等是在使用者空間排程,不由 OS 直接排(見 執行緒模型筆記)。
問題 3:一個行程可以沒有執行緒嗎?
不行。行程至少有一條主執行緒,否則沒有東西可執行。
問題 4:為什麼執行緒切換比行程切換快?
執行緒共享位址空間,切換時不必更換整套記憶體映射(page table)與大量資源;行程切換要切換位址空間,成本高(見 情境切換)。
問題 5:多執行緒一定比較快嗎?
不一定。CPU 密集且核心數有限時,過多執行緒反而因情境切換與鎖競爭變慢;I/O 密集才最受益。還要看語言限制(如 Python GIL)。
總結
核心要點
- 三層:程式(靜態檔)→ 行程(執行實例,資源單位)→ 執行緒(執行流,排程單位)
- 行程互相隔離,溝通靠 IPC;執行緒共享位址空間,溝通快但需同步
- 執行緒共享 code / heap / 檔案,獨立 stack / PC / 暫存器
- 執行緒用於並發、善用多核、保持反應性,且比多行程省資源
- 共享記憶體導致 race / deadlock,需同步機制
- 生命週期:New → Ready ⇄ Running ⇄ Blocked → Terminated
快速參考
| 概念 | 角色 | 隔離 |
|---|---|---|
| Program | 靜態可執行檔 | — |
| Process | 資源擁有單位 | 互相隔離(IPC 溝通) |
| Thread | CPU 排程單位 | 共享行程位址空間 |
建立日期:2026-06-18