目錄
01-5. Process vs Thread 完整對比
⏱️ 閱讀時間: 10 分鐘 🎯 難度: ⭐⭐ (簡單)
🎯 本篇重點
完整對比 Process 和 Thread 的差異,理解各自的優缺點和適用場景。
🤔 一句話解釋
Process: 擁有獨立記憶體空間的執行單位 Thread: 共享記憶體空間的輕量級執行單位
🏢 用公司來比喻
Process = 獨立的公司
公司 A(Process A)
├─ 辦公室(獨立記憶體空間)
├─ 資產(獨立資源)
├─ 員工(Thread)
└─ 財務(獨立帳戶)
公司 B(Process B)
├─ 辦公室(獨立記憶體空間)
├─ 資產(獨立資源)
├─ 員工(Thread)
└─ 財務(獨立帳戶)
公司 A 和 B 完全獨立,互不干擾Thread = 公司內的員工
公司 A(Process)
├─ 辦公室(共享記憶體空間)
│ ├─ 員工 1(Thread 1)
│ ├─ 員工 2(Thread 2)
│ └─ 員工 3(Thread 3)
│
├─ 共用資源
│ ├─ 會議室
│ ├─ 影印機
│ └─ 茶水間
│
└─ 所有員工共享這些資源!📊 核心差異對比
記憶體空間
| 特性 | Process | Thread |
|---|---|---|
| 記憶體空間 | 獨立 | 共享 |
| Code Segment | 各自獨立 | 共享同一份 |
| Data Segment | 各自獨立 | 共享 |
| Stack | 各自獨立 | 各自獨立 |
| Heap | 各自獨立 | 共享 |
# Process:獨立記憶體
from multiprocessing import Process
global_var = 0 # 每個 Process 有自己的 global_var
def process_task():
global global_var
global_var += 1
print(f"Process: global_var = {global_var}")
p1 = Process(target=process_task)
p2 = Process(target=process_task)
p1.start(); p2.start()
p1.join(); p2.join()
# 輸出:
# Process: global_var = 1
# Process: global_var = 1 ← 各自獨立!# Thread:共享記憶體
from threading import Thread
global_var = 0 # 所有 Thread 共享同一個 global_var
def thread_task():
global global_var
global_var += 1
print(f"Thread: global_var = {global_var}")
t1 = Thread(target=thread_task)
t2 = Thread(target=thread_task)
t1.start(); t2.start()
t1.join(); t2.join()
# 輸出:
# Thread: global_var = 1
# Thread: global_var = 2 ← 共享記憶體!創建成本
| 操作 | Process | Thread |
|---|---|---|
| 創建時間 | ~1-5 ms | ~10-100 μs |
| 記憶體消耗 | ~10-100 MB | ~1-10 MB |
| Context Switch | 慢(~61 μs) | 快(~1 μs) |
import time
from multiprocessing import Process
from threading import Thread
# 測試創建 1000 個 Process
start = time.time()
processes = [Process(target=lambda: None) for _ in range(1000)]
for p in processes:
p.start()
for p in processes:
p.join()
print(f"Process: {time.time() - start:.2f} 秒")
# 測試創建 1000 個 Thread
start = time.time()
threads = [Thread(target=lambda: None) for _ in range(1000)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"Thread: {time.time() - start:.2f} 秒")輸出範例:
Process: 5.23 秒
Thread: 0.12 秒 ← Thread 快 40 倍!通訊方式
| 通訊方式 | Process | Thread |
|---|---|---|
| 複雜度 | 複雜(需要 IPC) | 簡單(直接存取) |
| 速度 | 慢 | 快 |
| 安全性 | 高(隔離) | 低(需要同步) |
# Process:需要使用 IPC(例如:Queue)
from multiprocessing import Process, Queue
def producer(queue):
for i in range(5):
queue.put(f"資料 {i}")
def consumer(queue):
while not queue.empty():
data = queue.get()
print(f"收到: {data}")
queue = Queue()
p1 = Process(target=producer, args=(queue,))
p2 = Process(target=consumer, args=(queue,))
p1.start(); p2.start()
p1.join(); p2.join()# Thread:直接共享變數
from threading import Thread
shared_data = []
def producer():
for i in range(5):
shared_data.append(f"資料 {i}")
def consumer():
while len(shared_data) > 0:
data = shared_data.pop(0)
print(f"收到: {data}")
t1 = Thread(target=producer)
t2 = Thread(target=consumer)
t1.start(); t2.start()
t1.join(); t2.join()穩定性與隔離
| 特性 | Process | Thread |
|---|---|---|
| 錯誤隔離 | ✅ 一個崩潰不影響其他 | ❌ 一個崩潰全部崩潰 |
| 記憶體洩漏 | ✅ 隔離 | ❌ 影響整個 Process |
| 安全性 | ✅ 高 | ⚠️ 需要同步機制 |
# Process:錯誤隔離
from multiprocessing import Process
def task_crash():
raise Exception("Process 崩潰!")
def task_ok():
print("我還活著!")
p1 = Process(target=task_crash)
p2 = Process(target=task_ok)
p1.start(); p2.start()
p1.join(); p2.join()
# 輸出:
# 我還活著! ← p2 不受 p1 崩潰影響# Thread:一個崩潰全部崩潰
from threading import Thread
def task_crash():
raise Exception("Thread 崩潰!")
def task_ok():
print("我還活著!")
t1 = Thread(target=task_crash)
t2 = Thread(target=task_ok)
t1.start(); t2.start()
# 整個程式崩潰!🎯 優缺點總結
Process 的優缺點
優點:
- ✅ 獨立記憶體:一個 Process 崩潰不影響其他
- ✅ 真正並行:多核 CPU 可同時執行(不受 Python GIL 限制)
- ✅ 安全:Process 間完全隔離
- ✅ 穩定:錯誤不會擴散
缺點:
- ❌ 創建成本高:記憶體、時間消耗大
- ❌ 通訊複雜:需要使用 IPC(Queue, Pipe, Socket)
- ❌ Context Switch 慢:切換成本高
- ❌ 記憶體消耗大:每個 Process 獨立空間
Thread 的優缺點
優點:
- ✅ 創建成本低:快速、記憶體消耗小
- ✅ 通訊簡單:直接共享變數
- ✅ Context Switch 快:切換成本低
- ✅ 資源共享:共享 Heap、Data Segment
缺點:
- ❌ GIL 限制(Python):無法真正並行(CPU 密集型)
- ❌ 錯誤擴散:一個 Thread 崩潰,整個 Process 崩潰
- ❌ 同步複雜:需要 Lock、Semaphore 避免 Race Condition
- ❌ 除錯困難:多 Thread 問題難以重現
🌳 決策樹:何時用 Process?何時用 Thread?
開始
│
├─ 任務類型?
│
├─ CPU 密集型(大量計算)?
│ └─ YES → 用 Process ✅
│ • 繞過 Python GIL
│ • 真正並行計算
│ • 範例:數據分析、機器學習
│
├─ I/O 密集型(大量等待)?
│ │
│ ├─ 需要共享大量資料?
│ │ └─ YES → 用 Thread ✅
│ │ • 共享記憶體
│ │ • 通訊簡單
│ │ • 範例:Web 爬蟲、API 呼叫
│ │
│ └─ 需要高穩定性?
│ └─ YES → 用 Process ✅
│ • 錯誤隔離
│ • 更穩定
│
└─ 混合型?
└─ 根據瓶頸選擇🔍 實際案例對比
案例 1:CPU 密集型(數據分析)
import time
from multiprocessing import Process
from threading import Thread
def cpu_task():
"""CPU 密集計算"""
for i in range(10000000):
_ = i * i
# 使用 Process(真正並行)
start = time.time()
processes = [Process(target=cpu_task) for _ in range(4)]
for p in processes:
p.start()
for p in processes:
p.join()
print(f"Process: {time.time() - start:.2f} 秒")
# 使用 Thread(受 GIL 限制)
start = time.time()
threads = [Thread(target=cpu_task) for _ in range(4)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"Thread: {time.time() - start:.2f} 秒")輸出(4 核 CPU):
Process: 2.5 秒 ← 4 核並行,快!
Thread: 9.8 秒 ← GIL 限制,慢!結論:CPU 密集型用 Process
案例 2:I/O 密集型(網路請求)
import time
import requests
from multiprocessing import Process
from threading import Thread
def io_task():
"""I/O 密集任務"""
response = requests.get('https://httpbin.org/delay/1')
# 使用 Process
start = time.time()
processes = [Process(target=io_task) for _ in range(10)]
for p in processes:
p.start()
for p in processes:
p.join()
print(f"Process: {time.time() - start:.2f} 秒")
# 使用 Thread
start = time.time()
threads = [Thread(target=io_task) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"Thread: {time.time() - start:.2f} 秒")輸出:
Process: 1.5 秒 ← 創建成本高
Thread: 1.1 秒 ← 創建快,效果相同結論:I/O 密集型用 Thread(創建成本低)
📋 快速選擇表
| 場景 | 推薦 | 理由 |
|---|---|---|
| 數據分析 | Process | CPU 密集,需要真正並行 |
| 機器學習訓練 | Process | CPU 密集,繞過 GIL |
| Web 爬蟲 | Thread | I/O 密集,共享資料方便 |
| API 呼叫 | Thread | I/O 密集,創建成本低 |
| 檔案處理 | Thread | I/O 密集 |
| 影像處理 | Process | CPU 密集 |
| 資料庫查詢 | Thread | I/O 密集 |
| Web Server | Process | 穩定性、隔離性 |
| 長時間運行服務 | Process | 穩定性 |
| 短期批次任務 | Thread | 創建成本低 |
🔧 Python 實戰建議
最佳實踐組合
# 組合使用:Process Pool + Thread Pool
from multiprocessing import Pool
from concurrent.futures import ThreadPoolExecutor
def cpu_task(data):
"""CPU 密集"""
return complex_calculation(data)
def io_task(url):
"""I/O 密集"""
return requests.get(url)
# CPU 密集:使用 Process Pool
with Pool(processes=4) as pool:
results = pool.map(cpu_task, data_list)
# I/O 密集:使用 Thread Pool
with ThreadPoolExecutor(max_workers=20) as executor:
results = executor.map(io_task, url_list)✅ 重點回顧
核心差異:
- Process:獨立記憶體,真正並行,成本高
- Thread:共享記憶體,GIL 限制,成本低
選擇原則:
- CPU 密集 → Process
- I/O 密集 → Thread(或 asyncio)
- 需要穩定性 → Process
- 需要快速創建 → Thread
關鍵理解:
- ✅ Process 適合計算密集、需要隔離的場景
- ✅ Thread 適合 I/O 密集、需要共享資料的場景
- ✅ Python GIL 限制 Thread 的 CPU 並行能力
- ✅ 實際應用中常常組合使用
上一篇: 01-4. Context Switch 詳解 下一篇: 02-1. Thread 是什麼
最後更新:2025-01-04