對於Python的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

那么CPython實現中的GIL又是什么呢?GIL全稱Global Interpreter Lock.

為什么會有GIL

由於物理上得限制,各CPU廠商在核心頻率上的比賽已經被多核所取代。為了更有效的利用多核處理器的性能,就出現了多線程的編程方式,而隨之帶來的就是線程間數據一致性和狀態同步的困難。即使在CPU內部的Cache也不例外,為了有效解決多份緩存之間的數據同步時各廠商花費了不少心思,也不可避免的帶來了一定的性能損失。

Python當然也逃不開,為了利用多核,Python開始支持多線程。而解決多線程之間數據完整性和狀態同步,即數據安全,最簡單方法自然就是加鎖。 於是有了GIL這把超級大鎖,而當越來越多的代碼庫開發者接受了這種設定后,他們開始大量依賴這種特性(即默認python內部對象是thread-safe的,無需在實現時考慮額外的內存鎖和同步操作)。

慢慢的這種實現方式被發現是蛋疼且低效的。但當大家試圖去拆分和去除GIL的時候,發現大量庫代碼開發者已經重度依賴GIL而非常難以去除了。有多難?做個類比,像MySQL這樣的“小項目”為了把Buffer Pool Mutex這把大鎖拆分成各個小鎖也花了從5.5到5.6再到5.7多個大版為期近5年的時間,並且仍在繼續。MySQL這個背后有公司支持且有固定開發團隊的產品走的如此艱難,那又更何況Python這樣核心開發和代碼貢獻者高度社區化的團隊呢?

所以簡單的說GIL的存在更多的是歷史原因。如果推到重來,多線程的問題依然還是要面對,但是至少會比目前GIL這種方式會更優雅。

GIL的影響

從上文的介紹和官方的定義來看,GIL無疑就是一把全局排他鎖。毫無疑問全局鎖的存在會對多線程的效率有不小影響。甚至就幾乎等於Python是個單線程的程序

因為GIL,python只有一個GIL,運行python時,就要拿到這個鎖才能執行,在遇到I/O 操作時會釋放這把鎖。
在Python2中,如果是純計算的程序,沒有 I/O 操作,解釋器會每隔100次操作就釋放這把鎖,讓別的線程有機會 執行(這個次數可以通sys.setcheckinterval
來調整)同一時間只會有一個獲得GIL線程在跑,其他線程都處於等待狀態
1、如果是CPU密集型代碼(循環、計算等),由於計算工作量多和大,計算很快就會達到100,然后觸發GIL的釋放與在競爭,多個線程來回切換損耗資源,所以在多線程遇到CPU密集型代碼時,單線程會比多線程的快。 2、如果是I\O密集型代碼(文件處理、網絡爬蟲),開啟多線程實際上是並發(不是並行),IO操作會進行IO等待,線程A等待時,自動切換到線程B,這樣就提升了效率,比單線程快很多

而在python3.x中,GIL不使用ticks計數,改為使用計時器(執行時間達到閾值后,當前線程釋放GIL),這樣對CPU密集型程序更加友好,但依然沒有解決GIL導致的同一時間只能執行一個線程的問題,所以效率依然不盡如人意。

多核多線程比單核多線程更差,原因是單核下多線程,每次釋放GIL,喚醒的那個線程都能獲取到GIL鎖,所以能夠無縫執行,但多核下,CPU0釋放GIL后,其他CPU上的線程都會進行競爭,但GIL可能會馬上又被CPU0拿到,導致其他幾個CPU上被喚醒后的線程會醒着等待到切換時間后又進入待調度狀態,這樣會造成線程顛簸(thrashing),導致效率更低 

“python下想要充分利用多核CPU,就用多進程”,原因是什么呢?

原因是:每個進程有各自獨立的GIL,互不干擾,這樣就可以真正意義上的並行執行,(多線程並不是真正意義上的並行執行,只能算並發執行)所以在python中,多進程的執行效率優於多線程(僅僅針對多核CPU而言)。

由於現實生活中運行的總進程數量總是多於 CPU核心數量!因此,嚴格來說並沒有真正意義上的並行。現在運行的程序都是輪詢調度產生的並行假象,所以如何根據實際情況選擇多進程或是多線程。

總結

Python GIL其實是功能和性能之間權衡后的產物,它尤其存在的合理性,也有較難改變的客觀因素。從本分的分析中,我們可以做以下一些簡單的總結:

  1. 因為GIL的存在,只有IO密集型場景下的多線程會得到較好的性能,而在CPU密集型(計算密集型)或者高並發場景下,使用多進程效率會更快。
  2. GIL在較長一段時間內將會繼續存在,但是會不斷對其進行改進。
  3. GIL的全稱是全局解釋器鎖, 也就是一個解釋器一個鎖,解釋器也就是python.exe可執行文件,GIL的目的是確保每個進程中只有一個線程運行,所以多個進程之間是不會互相影響的,多進程確實可以用來削弱GIL的負面影響,但是對於IO密集型操作事務,多進程理論上也會比多線程快一點,但是因為多進程消耗的資源也比多線程大很多,所以說你只有少數的任務並發,你用多進程沒有問題,但是並發任務多的情況而且是IO密集型操作,用多線程就比多進程好的多,畢竟多線程占用資源少,比多進程更加便於管理,多進程的管理比多線程要復雜而且不穩定。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM