Python 3.14 Free Threading 完整教學:移除 GIL 實現真正多線程平行運算
什麼是 Free Threading?GIL 的前世今生
如果你用 Python 寫過任何多線程程式,一定有過這種沮喪的體驗:明明開了 8 個 thread,CPU 跑起來卻跟只用一個核心差不多。這不是你的問題,是 GIL 的問題。
GIL(Global Interpreter Lock,全域直譯器鎖)是 CPython 的一個設計決策,它確保任何時間只有一個 thread 能執行 Python bytecode。這個設計在 1990 年代初期有充分的理由——當時的記憶體管理模型需要這種保護機制。但三十年後的今天,當多核心 CPU 已經是標配,GIL 反而成了效能瓶頸。
Python 3.14 帶來了期待已久的 Free Threading 模式(也稱為 no-GIL 模式),讓 Python 終於能真正發揮多核心硬體的潛力。這不只是一個小更新,而是 Python 執行模型的根本性轉變。
Free Threading 的開發歷程
這個計畫的主要推手是 Meta 工程師 Sam Gross,他在 2021 年提出了 nogil 分支,並且實際證明了移除 GIL 的可行性。後來這個工作被納入官方計畫,以 PEP 703(Making the Global Interpreter Lock Optional in CPython)的形式正式推進。
Python 3.12 和 3.13 已經開始引入實驗性支援,而 Python 3.14 進一步穩定了這個功能,讓 Free Threading 從「實驗性」走向「可在生產環境謹慎評估」的階段。
如何安裝 Free Threading 版本
Free Threading 並不是預設開啟的——你需要安裝專門的 Free Threading 版本。目前有幾種方式:
使用 pyenv 安裝
# 安裝 pyenv(如果還沒有的話)
curl https://pyenv.run | bash
# 安裝 Python 3.14t(t 代表 free-threading)
pyenv install 3.14t
# 切換到 free-threading 版本
pyenv local 3.14t
# 確認版本
python --version
# Python 3.14.0 (free-threading build)
確認 Free Threading 已啟用
import sys
# 確認是否為 free-threading build
print(sys._is_gil_enabled()) # False 表示 GIL 已關閉
print(sys.version) # 應包含 "free-threading" 字樣
# 或是使用 sysconfig
import sysconfig
print(sysconfig.get_config_var('Py_GIL_DISABLED')) # 1 表示 GIL 停用
第一個 Free Threading 平行運算範例
讓我們直接用程式碼感受一下差異。以下是一個 CPU 密集型的計算任務:
import threading
import time
import sys
def compute_intensive(n):
result = 0
for i in range(n):
result += i * i
return result
def run_with_threads(num_threads, work_per_thread=10_000_000):
threads = []
start = time.perf_counter()
for _ in range(num_threads):
t = threading.Thread(target=compute_intensive, args=(work_per_thread,))
threads.append(t)
t.start()
for t in threads:
t.join()
elapsed = time.perf_counter() - start
return elapsed
print(f"GIL 啟用狀態: {sys._is_gil_enabled()}")
for n in [1, 2, 4, 8]:
t = run_with_threads(n)
print(f"{n} threads: {t:.3f}s")
在傳統 CPython(有 GIL)環境下,增加 thread 數量幾乎不會加速 CPU 密集型任務。但在 Free Threading 模式下,你會看到接近線性的效能提升——4 個 thread 大約快 3-4 倍,8 個 thread 快 6-7 倍(實際數字因硬體而異)。
實際效能測試:哪些場景受益最大
Free Threading 的效益不是一視同仁的,需要區分不同類型的工作:
CPU 密集型任務(最大受益)
數值計算、加密/解密、資料壓縮、影像處理等純 Python 計算任務,在 Free Threading 下可以看到近乎線性的多核心擴展:
import concurrent.futures
import hashlib
def hash_data(data: bytes) -> str:
result = ""
for _ in range(10000):
result = hashlib.sha256(data).hexdigest()
return result
data_chunks = [f"chunk_{i}".encode() * 1000 for i in range(16)]
with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
results = list(executor.map(hash_data, data_chunks))
print(f"完成 {len(results)} 個雜湊計算")
I/O 密集型任務(已有非同步替代方案)
對於網路請求、檔案 I/O 等場景,其實 asyncio 早就解決了這個問題,Free Threading 的幫助相對有限。但如果你的 codebase 已經是 threading 架構,Free Threading 仍有優化空間。
如果你還在評估後端框架,FastAPI 後端 API 開發教學 介紹了如何用 asyncio 處理高並發場景,這和 Free Threading 是互補的策略。
Free Threading 的重要警告:線程安全問題
這裡是很多人會踩的坑。移除 GIL 之後,你的程式碼不再自動受到保護。很多以前「靠 GIL 保護」的程式碼,在 Free Threading 環境下會出現競態條件(race condition)。
競態條件範例
import threading
class Counter:
def __init__(self):
self.value = 0
def increment(self):
# 在 Free Threading 下這不是原子操作!
self.value += 1
counter = Counter()
threads = []
for _ in range(1000):
t = threading.Thread(target=counter.increment)
threads.append(t)
t.start()
for t in threads:
t.join()
print(f"期望: 1000, 實際: {counter.value}") # 可能小於 1000!
正確的線程安全寫法
import threading
class SafeCounter:
def __init__(self):
self.value = 0
self._lock = threading.Lock()
def increment(self):
with self._lock:
self.value += 1
# 使用 Queue 進行線程間安全通訊
from queue import Queue
def producer(q: Queue, items: list):
for item in items:
q.put(item)
q.put(None) # 結束訊號
def consumer(q: Queue):
results = []
while True:
item = q.get()
if item is None:
break
results.append(item * 2)
return results
遷移現有程式碼的實用建議
如果你有現有的 Python 多線程程式碼,以下幾點是遷移的重點:
1. 審查所有共享狀態
找出所有 threads 之間共享的可變物件(list、dict、自定義 class 等),確保所有修改都有適當的鎖保護。
2. 使用線程安全的資料結構
from queue import Queue
import threading
# Queue 是線程安全的
task_queue = Queue(maxsize=100)
# 需要 lock 保護的 dict
shared_dict = {}
dict_lock = threading.RLock() # RLock 允許同一 thread 重複取得鎖
def safe_update(key, value):
with dict_lock:
shared_dict[key] = value
3. 靜態分析輔助
在開始遷移之前,先確保你的程式碼品質良好。Python Ruff Linter 教學 介紹了如何用 Ruff 快速找出潛在問題,配合 Python 自動化入門指南 建立完整的 CI/CD 管道,讓你的多線程程式碼更安全。
Free Threading 效能調優技巧
適當的 thread 數量
import os
import concurrent.futures
cpu_count = os.cpu_count()
print(f"可用 CPU 核心: {cpu_count}")
# CPU 密集型任務:thread 數 = CPU 核心數
# I/O 密集型任務:thread 數可以是 CPU 核心數的 5-10 倍
def cpu_task(data):
return sum(x**2 for x in data)
data_list = [range(1_000_000) for _ in range(cpu_count)]
with concurrent.futures.ThreadPoolExecutor(max_workers=cpu_count) as executor:
results = list(executor.map(cpu_task, data_list))
print(f"完成 {len(results)} 個計算任務")
減少鎖競爭
過多的鎖不只影響安全性,也會拖累效能。設計上應盡量避免 threads 之間頻繁爭搶同一把鎖:
import threading
class PartitionedCounter:
def __init__(self, num_partitions=16):
self.partitions = [0] * num_partitions
self.locks = [threading.Lock() for _ in range(num_partitions)]
def increment(self, thread_id):
partition = thread_id % len(self.partitions)
with self.locks[partition]:
self.partitions[partition] += 1
def total(self):
return sum(self.partitions)
第三方套件的相容性現況
Free Threading 對整個 Python 生態系都是挑戰。目前的狀況是:
- NumPy:已在積極測試 Free Threading 相容性,部分操作已支援
- pandas:相容性工作進行中,建議先觀望
- SQLAlchemy:核心部分已有 thread-safe 設計,較容易遷移
- requests / httpx:本身就是 thread-safe,相容性良好
- C extension 模組:需要更新才能在 Free Threading 下安全運作
判斷方式:安裝套件後,查看是否有 free-threaded wheel 可用。PyPI 上已有許多主流套件開始提供 ft(free-threaded)版本的 wheel。
什麼時候適合用 Free Threading?
說了這麼多,Free Threading 並不是萬靈丹。以下是個人的實際建議:
適合場景:科學計算、機器學習資料前處理、影像/音訊批次處理、自行開發的 CPU 密集型工具。
暫緩場景:依賴大量第三方 C 擴展的專案(相容性未確認前)、已經用 asyncio 架構的高並發服務(沒有顯著優勢)、對穩定性要求極高的生產環境(Free Threading 仍在持續改善中)。
Free Threading 是 Python 效能進化的重要里程碑。雖然現在(2026 年初)生態系相容性仍需時間完善,但對於自行控制的程式碼,現在就可以開始實驗和準備遷移了。Python 3.14 Free Threading 無GIL多線程平行運算的潛力已經非常清晰——這次,Python 終於可以真正榨乾你的 CPU 了。
繼續閱讀
Python uv 專案管理完整教學:取代 pip 與 Poetry 的新一代開發工具
uv 是 Astral 團隊用 Rust 打造的 Python 專案管理工具,速度比 pip 快 10-100 倍,能一次取代 pip、Poetry、pyenv、virtualenv 等多個工具。這篇教學從安裝到專案管理、遷移指南一次搞懂。
相關文章
你可能也喜歡
探索其他領域的精選好文