目次
1. 基礎篇:什麼是 Python 的多進程?
1.1 什麼是多進程?
多進程是一種 同時執行多個進程(獨立的執行單位)的技術。在 Python 中,可以透過 multiprocessing
模組輕鬆實作多進程。
多進程的特點
- 每個進程擁有獨立的記憶體空間
- 可以最大化利用 CPU 核心
- 需要進程之間的通訊(使用
Queue
或Pipe
)
具體的使用情境
- 涉及大量計算的處理(機器學習、數值模擬)
- 需要充分運用 CPU 的任務(影像處理、資料分析)
1.2 與多執行緒的差異
Python 也有「多執行緒」這種並行處理機制。那麼多進程與多執行緒有什麼不同呢?
項目 | 多進程 | 多執行緒 |
---|---|---|
記憶體共享 | 不共享(各自獨立) | 共享(同一進程內) |
GIL 的影響 | 不受影響 | 受影響 |
適合 CPU 密集任務 | ◎ | ✕ |
適合 I/O 密集任務 | △ | ◎ |
資料傳遞方式 | 需使用 Queue 或 Pipe | 可直接使用共享記憶體 |
什麼是 GIL(Global Interpreter Lock)?
Python 的標準直譯器(CPython)中有一個稱為 GIL 的機制,即使使用多執行緒,也無法同時執行多個執行緒。因此,若想充分利用 CPU,建議使用多進程。
1.3 使用 Python 實作簡單的多進程範例
import multiprocessing
import time
def worker(n):
print(f"プロセス {n} 開始")
time.sleep(2)
print(f"プロセス {n} 終了")
if __name__ == "__main__":
process_list = []
# 3つのプロセスを作成
for i in range(3):
p = multiprocessing.Process(target=worker, args=(i,))
process_list.append(p)
p.start()
# 全てのプロセスが終了するまで待機
for p in process_list:
p.join()
print("全プロセス終了")
1.4 使用多進程時的注意事項
1. 在 Windows 系統上必須加上 if __name__ == "__main__":
在 Windows 環境中,使用 multiprocessing.Process()
時,若未加上 if __name__ == "__main__":
會發生錯誤。
錯誤的範例(會出現錯誤)
import multiprocessing
def worker():
print("Hello from process")
p = multiprocessing.Process(target=worker)
p.start()
此程式碼在 Windows 系統中會報錯。
正確的範例
import multiprocessing
def worker():
print("Hello from process")
if __name__ == "__main__":
p = multiprocessing.Process(target=worker)
p.start()
p.join()
加上 if __name__ == "__main__":
即可在 Windows 上正確執行。
1.5 小結
- 什麼是多進程? → 同時執行多個進程的方式
- 與多執行緒的差異 → 不受 GIL 限制,適合 CPU 密集型任務
- 在 Python 中的簡單範例 → 使用
multiprocessing.Process()
- Windows 注意事項 → 必須加上
if __name__ == "__main__":
2. 實戰篇:multiprocessing 模組的使用方式
2.1 multiprocessing 模組的概要
multiprocessing
模組是 Python 的標準函式庫,提供 基於進程的平行處理 功能。
透過這個模組,可以充分運用 CPU 核心,並避開 GIL 的限制。
multiprocessing 的主要功能
功能 | 說明 |
---|---|
Process | 建立並執行個別的進程 |
Queue | 用於進程之間的資料傳遞 |
Pipe | 在兩個進程間傳遞資料 |
Value & Array | 讓進程間使用共享記憶體 |
Pool | 建立進程池,有效率地執行平行處理 |
2.2 Process
類別的基本使用方法
要在 Python 中建立新的進程,可以使用 multiprocessing.Process
類別。
基本的進程建立範例
import multiprocessing
import time
def worker(n):
print(f"プロセス {n} 開始")
time.sleep(2)
print(f"プロセス {n} 終了")
if __name__ == "__main__":
p1 = multiprocessing.Process(target=worker, args=(1,))
p2 = multiprocessing.Process(target=worker, args=(2,))
p1.start()
p2.start()
p1.join()
p2.join()
print("全プロセス終了")
2.3 進程間通訊(Queue & Pipe)
使用 Queue 傳送與接收資料
import multiprocessing
def worker(q):
q.put("Hello from child process")
if __name__ == "__main__":
q = multiprocessing.Queue()
p = multiprocessing.Process(target=worker, args=(q,))
p.start()
p.join()
# 從子進程取得資料
print(q.get())
2.4 使用 Value
與 Array
進行共享記憶體
import multiprocessing
def worker(val, arr):
val.value = 3.14 # 修改共享記憶體中的值
arr[0] = 42 # 修改陣列中的值
if __name__ == "__main__":
val = multiprocessing.Value('d', 0.0) # 'd' 表示 double 型別
arr = multiprocessing.Array('i', [0, 1, 2]) # 'i' 表示整數型別
p = multiprocessing.Process(target=worker, args=(val, arr))
p.start()
p.join()
print(f"val: {val.value}, arr: {arr[:]}")
2.5 使用 Pool
類別進行進程管理
使用 Pool
進行平行處理
import multiprocessing
def square(n):
return n * n
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
results = pool.map(square, range(10))
print(results)
2.6 小結
- 使用
multiprocessing
模組可以 輕鬆實作平行處理 - 使用
Process
類別建立個別進程 - 使用
Queue
或Pipe
可以 在進程之間傳遞資料 - 透過
Value
或Array
可以 實現共享記憶體 - 使用
Pool
類別可以 高效處理大量資料

3. 進階篇:錯誤處理與效能最佳化
3.1 在 multiprocessing 中常見的錯誤與對策
錯誤 1:Windows 環境中未加上 if __name__ == "__main__":
的錯誤
錯誤訊息
RuntimeError: freeze_support() must be called if program is run in frozen mode
解決方法
import multiprocessing
def worker():
print("Hello from process")
if __name__ == "__main__": # 這一行是必要的
p = multiprocessing.Process(target=worker)
p.start()
p.join()
錯誤 2:PicklingError(無法在進程間傳遞函式)
錯誤訊息
AttributeError: Can't pickle local object 'main.<locals>.<lambda>'
解決方法
import multiprocessing
def square(x): # 使用全域函式
return x * x
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
results = pool.map(square, range(10)) # 避免使用 lambda
print(results)
錯誤 3:死鎖(進程無限等待)
解決方法
import multiprocessing
def worker(q):
q.put("data")
if __name__ == "__main__":
q = multiprocessing.Queue()
p = multiprocessing.Process(target=worker, args=(q,))
p.start()
print(q.get()) # 先取得資料
p.join() # 再等待進程結束
3.2 效能最佳化技巧
最佳化 1:適當設定進程數量
import multiprocessing
def worker(n):
return n * n
if __name__ == "__main__":
num_workers = multiprocessing.cpu_count() # 取得 CPU 核心數量
with multiprocessing.Pool(num_workers) as pool:
results = pool.map(worker, range(100))
print(results)
最佳化 2:使用 Pool.starmap()
import multiprocessing
def multiply(a, b):
return a * b
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
results = pool.starmap(multiply, [(1, 2), (3, 4), (5, 6)])
print(results)
最佳化 3:活用共享記憶體
import multiprocessing
import ctypes
def worker(shared_array):
shared_array[0] = 99 # 修改共享記憶體的值
if __name__ == "__main__":
shared_array = multiprocessing.Array(ctypes.c_int, [1, 2, 3]) # 建立共享記憶體
p = multiprocessing.Process(target=worker, args=(shared_array,))
p.start()
p.join()
print(shared_array[:]) # [99, 2, 3]
3.3 小結
- 說明了
multiprocessing
中常見的 錯誤與解決方法 - 效能最佳化的重點:
- 正確設定進程數量
- 活用 starmap()
- 使用共享記憶體來加速處理
4. 常見問題:FAQ 與對策
4.1 應該使用 Python 的多進程還是多執行緒?
解答
- CPU 密集型(需要大量計算的處理) → 建議使用多進程(multiprocessing)
- I/O 密集型(檔案、網路等處理) → 建議使用多執行緒(threading)
處理類型 | 適合的平行處理方式 |
---|---|
CPU 密集型(數值運算、影像處理等) | 多進程(multiprocessing) |
I/O 密集型(檔案操作、API 請求等) | 多執行緒(threading) |
4.2 為什麼覺得 multiprocessing
很「慢」?
解答
- 建立進程的成本較高 → 使用
Pool
來重複利用進程 - 資料複製太多 → 使用共享記憶體(
Value
,Array
)來改善 - 處理大量的小任務 → 可以考慮改用
concurrent.futures.ThreadPoolExecutor
import multiprocessing
def worker(n):
return n * n
if __name__ == "__main__":
with multiprocessing.Pool(multiprocessing.cpu_count()) as pool:
results = pool.map(worker, range(100))
print(results)
4.3 如何在多進程中共享字典或列表?
解答
可以使用 multiprocessing.Manager()
來實現共享物件
import multiprocessing
def worker(shared_list):
shared_list.append(100) # 更新共享列表
if __name__ == "__main__":
with multiprocessing.Manager() as manager:
shared_list = manager.list([1, 2, 3])
p = multiprocessing.Process(target=worker, args=(shared_list,))
p.start()
p.join()
print(shared_list) # [1, 2, 3, 100]
4.4 使用 multiprocessing.Pool
時常見的錯誤與對策是什麼?
錯誤訊息 | 原因 | 對策 |
---|---|---|
AttributeError: Can't pickle local object | 傳遞了 lambda 或區域函式 | 改用全域函式 |
RuntimeError: freeze_support() must be called | 在 Windows 中漏寫 if __name__ == "__main__": | 加入 if __name__ == "__main__": |
EOFError: Ran out of input | Pool 中的某個進程異常中斷 | 確保正確呼叫 pool.close() 與 pool.join() |
4.5 要如何偵錯 Python 的多進程?
解答
可以使用 multiprocessing.log_to_stderr()
來輸出進程的日誌
import multiprocessing
import logging
def worker(n):
logger = multiprocessing.get_logger()
logger.info(f"プロセス {n} 実行中")
if __name__ == "__main__":
multiprocessing.log_to_stderr(logging.INFO) # 啟用日誌
p = multiprocessing.Process(target=worker, args=(1,))
p.start()
p.join()
5. 總結與延伸學習資源
5.1 文章總結
多進程的基礎知識
- 什麼是多進程? → 同時執行多個進程,最大化 CPU 利用率的技術
- 與多執行緒的差異
- 多進程 → 適合 CPU 密集型處理(例如數值運算、影像處理)
- 多執行緒 → 適合 I/O 密集型處理(例如檔案處理、網路通訊)
multiprocessing
模組的使用方法
- 使用
Process
類別來 建立個別的進程 - 透過
Queue
或Pipe
來 進行進程間資料傳輸 - 使用
Value
或Array
來 共享記憶體 - 利用
Pool
類別來 有效率地執行平行處理
錯誤處理與效能最佳化
- 常見錯誤處理方法
- 如果未加上
if __name__ == "__main__":
,在 Windows 中會出現錯誤 - 使用
lambda
或區域函式會導致 PicklingError - 忘記使用
Queue.get()
可能會 導致死鎖 - 效能最佳化技巧
- 使用
Pool
降低重複建立進程的成本 - 使用
starmap()
傳遞多個參數 - 使用
multiprocessing.shared_memory
減少資料複製開銷
5.2 延伸學習資源
1. Python 官方文件
2. 線上教學文章
5.3 展望未來應用
妥善運用 Python 的 multiprocessing
模組,能夠有效提升 CPU 使用效率,打造高效能的程式。
接下來可以學習的技術
- 非同步處理(asyncio) → 用於 I/O 密集型的平行處理
- concurrent.futures → 統一管理 ThreadPoolExecutor 與 ProcessPoolExecutor
5.4 結語
本篇文章從 基礎到實作、再到進階應用,詳細介紹了 Python 多進程 的完整知識。
希望你能將所學應用在實際專案中,發揮更高效能的開發實力!🚀