程式、行程與執行緒完全指南

釐清 Program / Process / Thread 三層關係、位址空間共享、為何需要執行緒,以及執行緒生命週期


目錄


三層關係:程式 / 行程 / 執行緒

這三個詞常被混用,但它們是清楚的三層:

程式 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

🔗相關文章