在 Python 開發圈流傳著一句經典建議:「Python 多線程是雞肋,推薦使用多進程」。許多開發者知其然,卻不知其所以然。本文將從底層機制出發,深入剖析全局解釋器鎖(GIL)如何限制多線程效能,並說明為何多進程能真正實現並行運算。
GIL(Global Interpreter Lock,全局解釋器鎖)是 CPython 解釋器為了保護內部數據結構(如引用計數)的執行緒安全而引入的互斥鎖。在 Python 設計之初,為了簡化記憶體管理並方便整合非執行緒安全的 C 擴展庫,開發團隊做出了這項妥協:同一時間僅允許一條線程持有 GIL 並執行 Python 位元組碼。
在單一 Python 進程中,GIL 猶如唯一的「通行證」。線程執行週期如下:
這意味著多核 CPU 上,Python 多線程始終無法實現真正並行,只能在單核上透過快速切換實現併發。頻繁的鎖獲取、釋放與上下文切換反而帶來額外開銷。
在多核架構下,情況更糟:CPU 0 釋放 GIL 後,所有核心上的線程同時競爭,但 GIL 往往又被 CPU 0 瞬間搶回。被喚醒的其他核心線程發現拿不到鎖,只能進入等待狀態,造成嚴重的線程顛簸,效率反而低於單核。
Python 3 改用計時器取代 ticks 計數,對 CPU 密集型略有優化,但無法突破「單進程單時刻單線程」的根本限制。
每個 Python 進程擁有獨立的記憶體空間與 GIL,進程間互不干擾。利用 multiprocessing 模組啟動多進程,即可讓多核 CPU 同時執行 Python 代碼,實現真正的並行運算。這是充分利用多核硬體資源的通用最佳實踐。
threading、asyncio 或協程框架multiprocessing、concurrent.futures.ProcessPoolExecutor 或外部 C/C++/Rust 擴展(如 NumPy、Pandas 內部已釋放 GIL)理解 GIL 本質,才能在架構選型時做出正確決策,避免在多核機器上陷入「多線程越多越慢」的陷阱。