為什么有人會說 Python 多執行緒是雞肋?知乎上有人提出這樣一個問題,在我們常識中,多行程、多執行緒都是通過并發的方式充分利用硬體資源提高程式的運行效率,怎么在 Python 中反而成了雞肋?

這里也要注意:不管你是為了Python就業還是興趣愛好,記住:專案開發經驗永遠是核心,如果你沒有2020最新python入門到高級實戰視頻教程,可以去小編的Python交流.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,里面很多新python教程專案,還可以跟老司機交流討教!
有同學可能知道答案,因為 Python 中臭名昭著的 GIL,GIL 是什么?為什么會有 GIL?多執行緒真的是雞肋嗎? GIL 可以去掉嗎?帶著這些問題,我們一起往下看,同時需要你有一點點耐心,
多執行緒是不是雞肋,我們先做個實驗,實驗非常簡單,就是將數字 "1億" 遞減,減到 0 程式就終止,這個任務如果我們使用單執行緒來執行,完成時間會是多少?使用多執行緒又會是多少?show me the code
# 任務
def decrement(n):
while n > 0:
n -= 1
復制代碼
單執行緒
import time
start = time.time()
decrement(100000000)
cost = time.time() - start
>>> 6.541690826416016
復制代碼
在我的4核 CPU 計算機中,單執行緒所花的時間是 6.5 秒,可能有人會問,執行緒在哪里?其實任何程式運行時,默認都會有一個主執行緒在執行,(關于執行緒與行程這里不展開,我會單獨開一篇文章)
多執行緒
import threading
start = time.time()
t1 = threading.Thread(target=decrement, args=[50000000])
t2 = threading.Thread(target=decrement, args=[50000000])
t1.start() # 啟動執行緒,執行任務
t2.start() # 同上
t1.join() # 主執行緒阻塞,直到t1執行完成,主執行緒繼續往后執行
t2.join() # 同上
cost = time.time() - start
>>>6.85541033744812
復制代碼
創建兩個子執行緒 t1、t2,每個執行緒各執行 5 千萬次減操作,等兩個執行緒都執行完后,主執行緒終止程式運行,結果,兩個執行緒以合作的方式執行是 6.8 秒,反而變慢了,按理來說,兩個執行緒同時并行地運行在兩個 CPU 之上,時間應該減半才對,現在不減反增,
是什么原因導致多執行緒不快反慢的呢?
原因就在于 GIL ,在 Cpython 解釋器(Python語言的主流解釋器)中,有一把全域解釋鎖(Global Interpreter Lock),在解釋器解釋執行 Python 代碼時,先要得到這把鎖,意味著,任何時候只可能有一個執行緒在執行代碼,其它執行緒要想獲得 CPU 執行代碼指令,就必須先獲得這把鎖,如果鎖被其它執行緒占用了,那么該執行緒就只能等待,直到占有該鎖的執行緒釋放鎖才有執行代碼指令的可能,
因此,這也就是為什么兩個執行緒一起執行反而更加慢的原因,因為同一時刻,只有一個執行緒在運行,其它執行緒只能等待,即使是多核CPU,也沒辦法讓多個執行緒「并行」地同時執行代碼,只能是交替執行,因為多執行緒涉及到上線文切換、鎖機制處理(獲取鎖,釋放鎖等),所以,多執行緒執行不快反慢,
什么時候 GIL 被釋放呢?
當一個執行緒遇到 I/O 任務時,將釋放GIL,計算密集型(CPU-bound)執行緒執行 100 次解釋器的計步(ticks)時(計步可粗略看作 Python 虛擬機的指令),也會釋放 GIL,可以通過 sys.setcheckinterval()設定計步長度,sys.getcheckinterval() 查看計步長度,相比單執行緒,這些多是多執行緒帶來的額外開銷
CPython 解釋器為什么要這樣設計?
多執行緒是為了適應現代計算機硬體高速發展充分利用多核處理器的產物,通過多執行緒使得 CPU 資源可以被高效利用起來,Python 誕生于1991年,那時候硬體配置遠沒有今天這樣豪華,現在一臺普通服務器32核64G記憶體都不是什么司空見慣的事,但是多執行緒有個問題,怎么解決共享資料的同步、一致性問題,因為,對于多個執行緒訪問共享資料時,可能有兩個執行緒同時修改一個資料情況,如果沒有合適的機制保證資料的一致性,那么程式最終導致例外,所以,Python之父就搞了個全域的執行緒鎖,不管你資料有沒有同步問題,反正一刀切,上個全域鎖,保證資料安全,這也就是多執行緒雞肋的原因,因為它沒有細粒度的控制資料的安全,而是用一種簡單粗暴的方式來解決,
這種解決辦法放在90年代,其實是沒什么問題的,畢竟,那時候的硬體配置還很簡陋,單核 CPU 還是主流,多執行緒的應用場景也不多,大部分時候還是以單執行緒的方式運行,單執行緒不要涉及執行緒的背景關系切換,效率反而比多執行緒更高(在多核環境下,不適用此規則),所以,采用 GIL 的方式來保證資料的一致性和安全,未必不可取,至少在當時是一種成本很低的實作方式,
那么把 GIL 去掉可行嗎?
還真有人這么干多,但是結果令人失望,在1999年Greg Stein 和Mark Hammond 兩位哥們就創建了一個去掉 GIL 的 Python 分支,在所有可變資料結構上把 GIL 替換為更為細粒度的鎖,然而,做過了基準測驗之后,去掉GIL的 Python 在單執行緒條件下執行效率將近慢了2倍,
Python之父表示:基于以上的考慮,去掉GIL沒有太大的價值而不必花太多精力,
小結
最后要注意:不管你是為了Python就業還是興趣愛好,記住:專案開發經驗永遠是核心,如果你沒有2020最新python入門到高級實戰視頻教程,可以去小編的Python交流.裙 :七衣衣九七七巴而五(數字的諧音)轉換下可以找到了,里面很多新python教程專案,還可以跟老司機交流討教!
CPython解釋器提供了GIL(全域解釋器鎖)保證執行緒資料同步,那么有了 GIL,我們還需要執行緒同步嗎?多執行緒在IO密集型任務中,表現又怎樣呢?歡迎大家留言
本文的文字及圖片來源于網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,著作權歸原作者所有,如有問題請及時聯系我們以作處理,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/152397.html
標籤:Python
上一篇:python實作簡單的聊天
下一篇:python執行緒加同步鎖
