-
1 # 你看我獨角獸嗎
-
2 # 軟體測試開發技術棧
剛好,在整理關於對Python GIL的理解,正好可以回答這個問題,貼一下。
GIL 的存在一直是富有爭議的,它導致 Python 程式無法真正利用現代作業系統的多程序特性。需要注意的是,對於 I/O 圖形處理、NumPy 數學計算這樣的耗時操作都發生在 GIL 之外,實際上基本不受影響,真正受影響的都是 Python 位元組碼的執行,GIL 會導致效能瓶頸的出現。總之,只有在使用純 Python 做 CPU 密集的多執行緒運算時 GIL 會是問題。
GIL是什麼
Python的程式碼執行由python虛擬機器(也叫直譯器主迴圈,CPython版本)來控制,Python在設計之初就考慮到在直譯器的主迴圈中,同時只有一個執行緒在執行。即每個CPU在任意時刻只有一個執行緒在直譯器中執行。對python虛擬機器訪問的控制由全域性解釋鎖GIL控制,正是這個鎖來控制同一時刻只有一個執行緒能夠執行。——(在單核CPU下的多執行緒其實都只是併發,不是並行) 。
併發與並行區別:
併發:兩個或多個事件在同一時間間隔發生,或者說交替做不同事件的能力,或者說不同的程式碼塊交替執行。並行:兩個或者多個事件在同一時刻發生,或者說同時做不同事件的能力,或者說不同的程式碼塊同時執行。併發和並行的意義:
併發和並行都可以處理“多工”,二者的主要區別在於是否是“同時進行”多個的任務。
但是涉及到任務分解(有先後依賴耦合度高的任務無法做到並行)、任務執行(可能要考慮互斥、鎖、共享等)、結果合併。
python 下的多執行緒
在Python多執行緒下,每個執行緒的執行方式:
獲取GIL切換到這個執行緒去執行執行程式碼把執行緒設定為睡眠狀態釋放GIL再次重複以上步驟在Python2中,在直譯器解釋執行任何 Python 程式碼時,都需要先獲得這把鎖才行(同一時間只會有一個獲得了 GIL 的執行緒在跑,其它的執行緒都處於等待狀態等著 GIL 的釋放),在遇到 I/O 操作時會釋放這把鎖。如果是純計算的程式,沒有 I/O 操作,直譯器會每隔 100 次操作就釋放這把鎖,讓別的執行緒有機會執行(這個次數可以透過 sys.setcheckinterval 來調整)也正是這種設定,是的多執行緒的CPU密集型計算非常雞肋,下面會講到為何如此。
而在python3中,GIL不使用ticks計數(100次,釋放GIL),改為使用計時器(執行時間達到15ms閾值後,當前執行緒釋放 GIL),使得執行計算的次數更多,釋放次數減少,這樣對CPU密集型程式更加友好,但依然沒有解決GIL導致的同一時間只能執行一個執行緒的問題,所以效率依然不盡如人意。
那麼是不是python的多執行緒是雞肋嘛?
CPU密集型(各種迴圈處理、計數等等),在這種情況下,ticks計數很快就會達到閾值,然後觸發GIL的釋放與再競爭(多個執行緒來回切換是需要消耗資源的),所以python下的多執行緒對 CPU密集型程式碼並不友好,會觸發相當頻繁的執行緒切換。
IO密集型(檔案處理、網路爬蟲等),多執行緒能夠有效提升效率(單執行緒下有IO操作會進行IO 等待,造成不必要的時間浪費,而開啟多執行緒能線上程A等待時,自動切換到執行緒B,可以不浪費 CPU的資源,從而能提升程式執行效率,一個執行緒獲得GIL傳送訊息,然後等待返回訊息(阻塞),Python此時釋放GIL, 其他執行緒得到GIL傳送訊息,然後同樣等待返回訊息(阻塞)......,這樣保證了IO傳輸過程時間的合理利用,減少了IO等待造成的資源浪費,提高IO傳輸效率)。所以python的多執行緒對IO密集型程式碼比較友好。
結論
I/O密集型使用多執行緒併發執行提高效率、計算密集型使用多程序(multiprocessing )並行執行提高效率。通常程式既包含IO操作又包含計算操作,那麼這種情況下,在開始併發任務之前,可以先進行測試,測試多執行緒、多程序哪個效率高就是用哪種方式。
請注意:多核多執行緒比單核多執行緒更差,多核多程序下,CPU1釋放GIL後,其他CPU上的執行緒都會進行競爭,但GIL可能會馬上又被CPU1拿到,CPU2釋放GIL後……,導致其他幾個CPU上被喚醒後的執行緒會醒著等待到切換時間後又進入待排程狀態,這樣會造成執行緒顛簸(thrashing),導致效率更低。
多執行緒下的CPU密集型計算也不是無藥可醫,可以利用ctypes繞過GIL,ctypes可以使py直接呼叫任意的C動態庫的匯出函式。所要做的只是把關鍵部分用 C/C++ 寫成 Python 擴充套件。而且,ctypes會在呼叫C函式前釋放GIL。
同時,可以瞭解下協程,又稱微執行緒。協程最大的優勢就是協程極高的執行效率。因為子程式切換不是執行緒切換,而是由程式自身控制,因此,沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的效能優勢就越明顯。
第二大優勢就是不需要多執行緒的鎖機制,因為只有一個執行緒,也不存在同時寫變數衝突,在協程中控制共享資源不加鎖,只需要判斷狀態就好了,所以執行效率比多執行緒高很多。
因為協程是一個執行緒執行,那怎麼利用多核CPU呢?最簡單的方法是多程序+協程,既充分利用多核,又充分發揮協程的高效率,可獲得極高的效能。
-
3 # zhangyiant
執行緒還是多執行緒,只是有把鎖,導致只有一個執行緒能執行,但是python會在多個執行緒之間做切換的,所以還是帶來一定的並行處理。如果是單核處理器的話,效能不會有太大變化。如果是多核的話,可以考慮起多個程序來解決。
寫再多的執行緒都繞不過直譯器是單執行緒,那不還等於是單執行緒。且執行緒切換消耗,既然這樣,python還有必要進行多執行緒程式設計嗎?
回覆列表
CPython(標準的python實現)有一個名為GIL(全域性直譯器鎖)的東西,它阻止兩個執行緒在同一個程式中同時執行。 有些人對此感到不安,而其他人則狠狠地為此辯護。 但是,有一些解決方法,像Numpy這樣的庫透過在C中執行外部程式碼來繞過這個限制。
何時使用執行緒與程序?程序加速了CPU密集型的Python操作,因為它們受益於多個核心並避免使用GIL。
執行緒最適合IO任務或涉及外部系統的任務,因為執行緒可以更有效地組合他們的工作。 程序需要挑選他們的結果來組合它們需要時間。
由於GIL,執行緒在python中沒有為CPU密集型任務提供任何好處。而對於像Dot Product這樣的某些操作,Numpy可以解決Python的GIL並且並行執行程式碼。
I/O的多執行緒使用應用程式的大部分時間都花在I/O上。無論是磁碟I/O還是網路I/O。
例如,對於Web應用程式,大多數情況下是處理資料庫。因此,在大多數現代應用程式中,最大的瓶頸是I/O。以下是開啟4個執行緒後效果,實際上效果還是很顯著的。
這意味著應用程式大部分都在等待,因此即使python沒有GIL,也不是所有應用程式都會一直執行(使用cpu,因為它們將等待I/O完成)。
所以,在大多數應用程式執行緒在I / O上等待的情況下,其他執行緒可以獲取CPU,從而提高效能。如果python沒有多執行緒,那麼其他一些執行緒無法獲得cpu因此浪費時間。現在至少當一個執行緒正在等待I/O時(大部分應用程式都是這種情況),其餘的執行緒都可以工作,那麼多執行緒還是有它存在的必要,所以這就是我們沒有看到GIL所擁有問題的原因。
當然如果你的應用程式是CPU密集型的,那麼在python中確實沒有太多的執行緒可以提供使用。