導語
python
在編程世界里,Python 語言以其簡潔的語法和龐大的生態(tài)系統(tǒng),贏得了無數(shù)開發(fā)者的青睞。然而,一個長期存在的設計卻讓它在多核處理器時代備受詬病,那就是“全局解釋器鎖”(Global Interpreter Lock,簡稱 GIL)。許多來自其他編程語言的軟件工程師會因此得出一個結論:“Python 很慢!”。如今,這場曠日持久的爭論終于迎來了歷史性的轉折。一項名為 PEP 703 的提案已被 Python 社區(qū)正式接受,計劃在未來幾年內(nèi),逐步移除 GIL。
這不僅僅是一次技術更新,它預示著 Python 即將解開長久以來束縛其多線程并行能力的枷鎖。本文將帶您深入了解 GIL 的前世今生,剖析它為何成為性能瓶頸,并詳細解讀 PEP 703 提案將如何引領 Python 邁入一個真正的多核新時代。更重要的是,我們將提供一份詳盡的實操指南,教您如何在自己的電腦上安裝并親身體驗沒有 GIL 的 Python。
一、GIL 是什么?一個愛恨交織的歷史遺產(chǎn)
要理解這場變革的意義,我們首先需要弄明白 GIL 究竟是什么。
簡單來說,全局解釋器鎖(GIL)本質(zhì)上是一個互斥鎖(mutex),它的核心作用是確保在任何一個時刻,只有一個線程能夠執(zhí)行 Python 的字節(jié)碼。想象一下,Python 解釋器是一個工作間,多個線程是多個工人,而 GIL 就是這個工作間唯一的門禁卡。無論有多少個工人(線程)排隊,只有拿到門禁卡的那一個才能進入工作間干活,其他工人則必須在門外等待。
這個設計誕生于上世紀 90 年代初期,當時的主流計算機普遍使用單核 CPU。在那個年代,GIL 的存在是一個非常明智的選擇。它極大地簡化了 Python 語言自身的設計,為開發(fā)者提供了一種簡單直接的線程安全保障,避免了復雜的內(nèi)存管理問題。同時,它也簡化了 Python 與 C 語言編寫的擴展庫(C extensions)的集成過程,這在很大程度上促進了 Python 生態(tài)的早期繁榮和穩(wěn)定,為其快速贏得開發(fā)者社群的喜愛奠定了基礎。可以說,在特定的歷史時期,GIL 是 Python 成功的功臣之一。
然而,時間快進到 2025 年的今天,單核 CPU 早已成為歷史的塵埃,多核處理器成為了個人電腦和服務器的標配。曾經(jīng)的優(yōu)勢,如今卻成了 Python 發(fā)展的最大掣肘之一。
二、時代變了,GIL 為何成為性能瓶頸?
任何一項技術決策,都帶有其時代背景的烙印,也必然會帶來相應的后果。GIL 這個大膽的語言設計選擇,其最直接的后果就是:Python 的多線程無法真正實現(xiàn)并行計算。
在一個擁有 8 核 CPU 的現(xiàn)代計算機上,理論上我們可以同時運行 8 個計算任務。但是,由于 GIL 的存在,一個 Python 進程即使創(chuàng)建了 8 個線程,這 8 個線程也無法同時利用這 8 個 CPU 核心。它們依然需要排隊爭搶唯一的 GIL,本質(zhì)上還是在單個核心上進行著高速的、輪流的“偽并行”執(zhí)行。這使得 Python 在處理 CPU 密集型(CPU-bound)的多線程任務時,表現(xiàn)得非常低效。例如,進行大規(guī)模科學計算、數(shù)據(jù)分析或者復雜的算法處理時,多線程帶來的性能提升微乎其微。
為了繞過這個巨大的限制,開發(fā)者社區(qū)在過去幾十年里探索出了多種變通方案。
- 多進程(Multiprocessing):既然一個進程內(nèi)的多線程無法并行,那就創(chuàng)建多個獨立的進程。每個進程都有自己獨立的 GIL 和內(nèi)存空間,操作系統(tǒng)可以將這些進程分配到不同的 CPU 核心上,從而實現(xiàn)真正的并行計算。
- 使用外部庫:利用像 NumPy 或 Cython 這樣的庫,將計算密集型的任務“外包”給由 C 或其他編譯型語言編寫的底層代碼執(zhí)行。這些底層代碼不受 GIL 的限制,可以充分利用多核性能。
雖然這些變通方案在實踐中確實有效,但它們也帶來了新的問題:引入了額外的復雜性、進程間通信的開銷以及更高的資源消耗。這就像是為了解決一個問題,又不得不引入好幾個新的工具和流程,增加了整個項目的維護難度。
因此,在 Python 社區(qū)內(nèi)部,徹底移除 GIL 的呼聲從未停止。然而,這并非易事。就連 Python 的創(chuàng)造者 Guido van Rossum 也曾撰文坦言,移除 GIL 極其困難。他表示,如果有一個方案能在不損害單線程程序性能、不破壞向后兼容性的前提下移除 GIL,他會非常歡迎,但要同時滿足這些條件是極為苛刻的挑戰(zhàn)。
正因如此,多年來 Python 社區(qū)一直沒有一個明確且可行的計劃來移除 GIL,直到 PEP 703 方案的出現(xiàn),才真正帶來了破局的希望。
三、PEP 703:一個沒有 GIL 的勇敢新世界
PEP 703 是由開發(fā)者 Sam Gross 提出并被社區(qū)接受的一項提案,其核心目標是讓 GIL 成為一個可選項。
需要明確的是,該提案并非要立即在所有 Python 版本中廢除 GIL,而是引入了一個新的編譯時選項 --disable-gil。開發(fā)者可以通過這個構建配置標志,來編譯一個不包含 GIL 的、實驗性的 Python 版本。
Sam Gross 的創(chuàng)新方法成功地解決了過去移除 GIL 時遇到的幾個核心障礙:
- 有偏向的引用計數(shù) (Biased Reference Counting):這是解決 GIL 移除后線程安全內(nèi)存管理的關鍵。它通過為每個線程設置本地的引用計數(shù)器,來減少多線程環(huán)境下頻繁加鎖帶來的性能開銷。
- 不朽對象 (Immortal Objects):對于像整數(shù)、單例等頻繁使用且不可變的對象,將它們標記為“不朽”,使其引用計數(shù)永遠不會改變。這樣一來,就無需在多線程訪問這些對象時進行不必要的加鎖操作,從而提升效率。
- 線程安全的內(nèi)存管理 (Thread-safe Memory Management):采用像 mimalloc 這樣的現(xiàn)代內(nèi)存分配器,來高效地處理并發(fā)環(huán)境下的內(nèi)存分配和回收操作,確保穩(wěn)定性和性能。
Python 指導委員會已經(jīng)接受了 PEP 703,并規(guī)劃了一個謹慎的、分階段的實施路線圖:
- 第一階段:Python 3.13 (2024年已發(fā)布):提供一個可選的、無 GIL 的構建版本。此階段的主要目的是為了測試和收集社區(qū)的反饋,通過 --disable-gil 編譯標志來啟用。
- 第二階段:過渡階段 (約 2026–2027年):計劃統(tǒng)一標準版和無 GIL 版的構建,屆時開發(fā)者或許可以在運行時通過開關來切換 GIL 的啟用狀態(tài),這將大大簡化開發(fā)和部署流程。
- 第三階段:未來目標 (約 2028年以后):讓無 GIL 的 Python 成為默認版本。到那時,Python 將徹底釋放其在多核處理器上的并行計算潛力,成為所有開發(fā)者的默認選擇。
理論說了很多,現(xiàn)在讓我們動手實踐一下,看看如何在自己的電腦上真正禁用 GIL。
以下步驟以 macOS 15.5 系統(tǒng)為例,其他操作系統(tǒng)上的流程應該類似,但具體操作可能略有不同。
第一步:下載 Python 3.13 安裝包
首先,我們需要訪問 Python 的官方網(wǎng)站,下載最新的 Python 3.13 安裝程序。請確保根據(jù)你的操作系統(tǒng)(Windows、macOS 等)選擇正確的版本。
第二步:在安裝過程中啟用實驗性功能
下載完成后,打開安裝程序。在“安裝類型”(Installation Type)這一步驟,你會看到一個關鍵選項:“Free-threaded Python [experimental]”(自由線程 Python [實驗性])。這個選項默認是未勾選的,你必須手動勾選它,然后再繼續(xù)完成安裝。
第三步:安裝 SSL 證書
安裝完成后,在訪達(Finder)中打開 /Applications/Python 3.13/ 目錄,找到并執(zhí)行一個名為“Install Certificates.command”的腳本文件。這一步是為了給新安裝的 Python 配置 SSL 根證書,以便它能正常進行網(wǎng)絡請求等操作。
第四步:驗證安裝結果
完成以上步驟后,你的系統(tǒng)中實際上安裝了兩個 Python 3.13 應用:一個標準的 Python 3.13 和一個實驗性的 Python 3.13t。其中,末尾帶 't' 的就是我們期待的無 GIL 版本。
要使用它,只需在終端(Terminal)中輸入 python3.13t 即可。
為了驗證 GIL 是否真的被禁用了,我們可以打開兩個終端窗口,分別運行標準版和無 GIL 版的 Python,并輸入以下代碼: import sysconfig sysconfig.get_config_var("Py_GIL_DISABLED")
你會看到,在 python3.13 中,該命令返回 False 或 0;而在 python3.13t 中,它會返回 True 或 1。這證明我們已經(jīng)成功進入了沒有 GIL 的世界!。
五、性能實測:無 GIL 的 Python 究竟快了多少?
為了直觀感受性能差異,我們來運行一個簡單的測試腳本。這個腳本會創(chuàng)建 4 個線程,每個線程都執(zhí)行一個 CPU 密集型的計算任務。
import threadingimport time# 一個消耗 CPU 的計算任務def cpu_bound_task(n, thread_id):count = 0for i in range(n):count += i*iN = 100000000 # 設定一個較大的計算量def run_with_threads():threads = []start = time.time()# 創(chuàng)建并啟動 4 個線程for i in range(4):t = threading.Thread(target=cpu_bound_task, args=(N, i))threads.append(t)t.start()# 等待所有線程執(zhí)行完畢f(xié)or t in threads:t.join()end = time.time()print(f'總耗時: {end - start:.2f} 秒')if __name__ == '__main__':run_with_threads()
測試結果:
- 使用標準 Python 3.13 運行:在終端輸入 python3.13 your_script_name.py。 得到的運行時間是13.51 秒
- 使用無 GIL 的 Python 3.13t 運行:在終端輸入 python3.13t your_script_name.py。 得到的運行時間是3.74 秒!。
結果一目了然。在多線程執(zhí)行 CPU 密集型任務時,無 GIL 的 Python 耗時僅為標準版的約 1/3.6。數(shù)字雄辯地證明了:沒有了 GIL,Python 終于實現(xiàn)了真正的多線程并行計算。
六、未來展望:開發(fā)者現(xiàn)在應該做什么?
“要不要 GIL”,這個問題在 Python 社區(qū)爭論了多年,在可預見的未來,它將不再是一個問題。
這場變革得到了社區(qū)的廣泛支持,尤其是來自數(shù)據(jù)科學和人工智能領域的工程師們,他們熱切期盼著這次性能飛躍。包括 Meta 在內(nèi)的一些科技巨頭,也已經(jīng)投入了大量資源來支持這一過渡。
那么,對于廣大的 Python 開發(fā)者來說,這意味著什么?我們現(xiàn)在應該怎么做?
最重要的一條建議是:現(xiàn)階段,請不要在生產(chǎn)環(huán)境中使用無 GIL 的 Python。
當前發(fā)布的 Python 3.13 無 GIL 版本,其定位僅僅是一個“實驗性”構建。關于它的兼容性、長期穩(wěn)定性以及整個生態(tài)系統(tǒng)(尤其是大量的第三方庫)的適配情況,都還存在許多不確定性,需要時間來逐步完善和檢驗。
但是,我們絕對應該對此保持密切關注。移除 GIL 是 Python 發(fā)展史上的一個里程碑事件,它正在真實地發(fā)生。開發(fā)者可以開始在本地環(huán)境中進行實驗,了解其特性,并思考如何在未來的項目中利用這一優(yōu)勢。
人生苦短,我用 Python。現(xiàn)在,你又多了一個選擇 Python 的理由。這場激動人心的變革已經(jīng)拉開序幕,讓我們拭目以待。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺“網(wǎng)易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.