深入理解Python中的GIL(全域性直譯器鎖)

2020-08-14 21:08:15

前言:本博文主要講解Python中的GIL(全域性直譯器鎖),本人經過查閱多個資料,摒棄一些較官方的描述,用自己的語言整理並加以補充,如果有描述有誤的地方,還望讀者在評論區指出,謝謝!

一、GIL是什麼

GIL:又稱全域性直譯器鎖。作用就是限制多執行緒同時執行,保證同一時間內只有一個執行緒在執行。執行緒非獨立的,所以同一進程裡執行緒是數據共用,當各個執行緒存取數據資源時會出現「競爭」狀態,即數據可能會同時被多個執行緒佔用,造成數據混亂,這就是執行緒的不安全。所以引進了互斥鎖,確保某段關鍵程式碼、共用數據只能由一個執行緒從頭到尾完整地執行。

其次我們需要明確的一點是GIL並不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。就好比C++是一套語言(語法)標準,但是可以用不同的編譯器來編譯成可執行程式碼。有名的編譯器例如GCC,INTEL C++,Visual C++等。Python也一樣,同樣一段程式碼可以通過CPython,PyPy,Psyco等不同的Python執行環境來執行。像其中的JPython就沒有GIL。然而因爲CPython是大部分環境下預設的Python執行環境。所以在很多人的概念裡CPython就是Python,也就想當然的把GIL歸結爲Python語言的缺陷。所以這裏要先明確一點:GIL並不是Python的特性,Python完全可以不依賴於GIL。

那麼在這裏就反映出一個問題:在我們的Python語言中多執行緒其實是假的多執行緒,它只會在一個CPU上執行。這又是爲什麼呢?因爲在Python上開啓多個執行緒,由於GIL的存在,每個單獨執行緒都會在競爭到GIL後才執行,這樣就幹預OS內部的進程(執行緒)排程,結果在多核CPU上:Python的多執行緒實際是序列執行的,並不會同一時間多個執行緒分佈在多個CPU上執行。 在这里插入图片描述

二、爲什麼會有GIL

簡單的來說:當初Python語言在設計的時候,市面上並沒有多核CPU,因此執行緒都是單執行緒的。隨着時間的推移、科技的發展,逐漸出現多核CPU,各CPU廠商在覈心頻率上的比賽已經被多核所取代,爲了更有效的利用多核處理器的效能,就出現了多執行緒的程式設計方式,而隨之帶來的就是執行緒間數據一致性和狀態同步的困難。

Python爲了利用多核CPU,開始支援多執行緒。而解決多執行緒之間數據完整性和狀態同步的最簡單方法自然就是加鎖,於是有了GIL這把超級大鎖。因爲有了GIL,所以我們的Python可以實現多進程,但是這是一個假的多進程,雖然它會利用多個CPU共同共同作業,但實則是利用一個CPU的資源。

但是這種GIL導致我們的多進程並不是真正的多進程,所以它的效率很低。但當大家試圖去拆分和去除GIL的時候,發現大量庫程式碼開發者已經重度依賴GIL而非常難以去除了。如果推到重來,多執行緒的問題依然還是要面對,但是至少會比目前GIL這種方式會更優雅。所以簡單的說:GIL的存在更多的是歷史原因。

三、GIL的副作用

Python的多執行緒在多核CPU上,只對於IO密集型計算產生正面效果;而當有至少有一個CPU密集型執行緒存在,那麼多執行緒效率會由於GIL而大幅下降。正因爲有了GIL的存在,我們Python的多執行緒效率纔會比較低,畢竟它不是真正的多執行緒。那麼此時,我們就可以考慮使用多進程去實現,因爲多進程是可以利用多核的CPU資源的。但是又有一個問題?多進程需要的資源較大,明顯不是最好的解決辦法,那麼如何高效的解決這一問題呢?

我們都知道Python它其實是一個「膠水」語言,它除了可以呼叫自己的模組。類庫之外,還可以呼叫C、C++等語言的很多模組、類庫。此時,我們只需載入動態庫,把多進程這塊,換成利用C語言去實現就可以了。

四、GIL的總結

  1. 因爲GIL的存在,只有IO Bound場景下的多執行緒會得到較好的效能。
  2. 如果對並行計算效能較高的程式可以考慮把核心部分也成C模組,或者索性用其他語言實現。
  3. GIL在較長一段時間內將會繼續存在,但是會不斷對其進行改進。

五、GIL面試題

描述Python GIL的概念, 以及它對Python多執行緒的影響?編寫一個多執行緒抓取網頁的程式,並闡明多執行緒抓取程式是否可比單執行緒效能有提升,並解釋原因。

Guido的宣告:It isn’t Easy to Remove the GIL

he language doesn’t require the GIL – it’s only the CPython virtual machine that has historically been unable to shed it.

參考答案:

  1. Python語言和GIL沒有任何關係。僅僅是由於歷史原因在Cpython虛擬機器(直譯器),難以移除GIL。
  2. GIL:全域性直譯器鎖。每個執行緒在執行的過程都需要先獲取GIL,保證同一時刻只有一個執行緒可以執行程式碼。
  3. 執行緒釋放GIL鎖的情況: 在IO操作等可能會引起阻塞的system call之前,可以暫時釋放GIL,但在執行完畢後,必須重新獲取GIL Python 3.x使用計時器(執行時間達到閾值後,當前執行緒釋放GIL)或Python 2.x,tickets計數達到100。
  4. Python使用多進程是可以利用多核的CPU資源的。
  5. 多執行緒爬取比單執行緒效能有提升,因爲遇到IO阻塞會自動釋放GIL鎖。
不染-何程龍 CSDN認證部落格專家 Python c#/.net SQL
何程龍,大學計算機資訊管理專業在讀,現就任後端開發工程師、數據庫工程師。曾擔任創新創業學院理事會會長,現爲CSDN部落格專家、簽約講師。帶過多批學員,因授課耐心細緻,通俗易懂,風趣幽默,富有激情,受學員一致好評。
現主要從事軟件開發及軟體教育培訓,具有豐富的實戰經驗,善於將複雜問題簡單化,擅長Web前端、JavaScript框架、Java併發、C#程式設計、Python Web、數據庫SQL等領域及技術。