溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python全局解釋器鎖能做什么

發(fā)布時間:2021-12-01 09:45:30 來源:億速云 閱讀:132 作者:iii 欄目:編程語言

本篇內(nèi)容主要講解“Python全局解釋器鎖能做什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“Python全局解釋器鎖能做什么”吧!

懸而未決的問題

每個領(lǐng)域都會有這么一個問題:它難度大、耗時多,僅僅是嘗試解決這個問題都會讓人震驚。整個社區(qū)在很久以前就放棄了這個問題,現(xiàn)在只有少數(shù)人在努力試圖解決它。對于初學(xué)者來說,解決這樣高難度的問題,會給他帶來足夠的聲譽。計算機科學(xué)領(lǐng)域中的  P = NP 就是這樣的問題。如果能用多項式時間復(fù)雜度解決這個問題,那簡直就可以改變世界了。Python 中最困難的問題比 P = NP  要容易一些,不過迄今仍然沒有一個滿意的答案,解決這個問題和解決 P = NP 問題一樣具有革命性。正因為如此, Python  社區(qū)會有如此多的人關(guān)注于這個的問題: “對于全局解釋器鎖(GIL)能做什么?”

Python 的底層

要理解 GIL 的含義,我們需要從 Python 的基礎(chǔ)說起。像 C++  這樣的語言屬于編譯型語言,顧名思義,該類型語言的代碼輸入到編譯器,由編譯器根據(jù)語言的語法進行解析,生成與語言無關(guān)的中間表示,***鏈接成由高度優(yōu)化的機器碼組成的可執(zhí)行程序。因為編譯器可以獲取全部代碼(或者是一大段相對獨立的代碼),所以編譯器可以對代碼進行深度優(yōu)化。這使得它可以對不同的語言結(jié)構(gòu)之間的交互進行推理,從而做出更有效的優(yōu)化。

相反,Python 是解釋型語言。代碼被輸入到解釋器來運行。解釋器在執(zhí)行之前對代碼一無所知;它只知道 Python  的規(guī)則,以及如何在執(zhí)行過程中動態(tài)地應(yīng)用這些規(guī)則。它也有一些優(yōu)化,但是和編譯型語言的優(yōu)化完全不同。由于解釋器不能很好地對代碼進行推導(dǎo),Python  的大部分優(yōu)化其實是解釋器本身的優(yōu)化。更快的解釋器自然意味著更快的程序運行速度,而這種優(yōu)化對開發(fā)者來說是免費的。也就是說,解釋器優(yōu)化后,開發(fā)者不用修改  Python 代碼就可以坐享優(yōu)化帶來的好處。

這是非常重要的一點,這里有必要在強調(diào)一下。在同等條件下,Python  程序的運行速度與解釋器的“速度”直接相關(guān)相關(guān)。無論開發(fā)者怎樣優(yōu)化自己的代碼,程序的執(zhí)行速度還是受限于解釋器的執(zhí)行效率。很明顯,這就是為什么做了如此多的工作去優(yōu)化  Python 解釋器。這大概是離 Python 開發(fā)者最近的免費的午餐。

免費午餐結(jié)束了

還是沒有結(jié)束?摩爾定律告訴了我們硬件提速的時間表,同時,整整一代程序員學(xué)會了如何在摩爾定律下編寫代碼。如果程序員寫了比較慢的代碼,最簡單的辦法通常是稍稍等待一下更快的處理器問世即可。事實上,摩爾定律仍然是并且會在很長一段時間內(nèi)是有效的,不過它生效的方式有了根本的變化。時鐘頻率不會穩(wěn)定增長到一個高不可攀的速度,取而代之的是通過多核來利用晶體管密度提高帶來的好處。想要程序能夠充分利用新處理器的性能,就必須按照并發(fā)方式對代碼進行重寫。

大部分開發(fā)者聽到“并發(fā)”通常會馬上想到多線程程序。目前,多線程仍是利用多核系統(tǒng)最常見的方式。多線程編程比傳統(tǒng)的“順序”編程要難很多,不過仔細的程序員可以在代碼中充分利用多線程的并發(fā)性。既然幾乎所有應(yīng)用廣泛的現(xiàn)代編程語言都支持多線程編程,語言在多線程方面的實現(xiàn)應(yīng)該是事后添加上去的。

意外的事實

現(xiàn)在我們來看一下問題的癥結(jié)所在。想要利用多核系統(tǒng),Python 必須支持多線程。作為解釋型語言,Python  的解釋器對多線程的支持必須是既安全又高效的。我們都知道多線程編程帶來的問題。解釋器必須避免不同的線程操作內(nèi)部共享的數(shù)據(jù)。同時還要保證用戶線程能完成盡量多的計算。

那么在不同線程同時訪問數(shù)據(jù)時,怎樣才能保護數(shù)據(jù)呢?答案是全局解釋器鎖。顧名思義,這是一個加在解釋器上的全局鎖(從互斥量或者類似意義上來看)。這種方式是很安全,但是(對于  Python 初學(xué)者來說)這也就意味著:對于任何 Python 程序,不論有多少線程,多少處理器,任何時候都只有一個線程在執(zhí)行。

許多人都是偶然發(fā)現(xiàn)這個事實。網(wǎng)上的討論組和留言板充斥著來自 Python 初學(xué)者和專家提出的類似的問題:為什么我全新的多線程 Python  程序運行得比其只有一個線程的時候還要慢?在問這個問題時,許多人還覺得自己像個傻瓜,因為如果程序確實是可并行的,那么兩個線程的程序顯然要比單線程要快。事實上,問及這個問題的次數(shù)實在太多了,Python  的專家們已經(jīng)為它準備了一個標準答案:不要使用多線程,請使用多進程。但這個答案比問題本身更加讓人困惑:難道我不能在 Python 中使用多線程?在 Python  這樣流行的語言中使用多線程究竟是有多糟糕,連專家都建議不要使用。是我哪里沒有搞明白嗎?

很遺憾,并不是。由于 Python  解釋器的設(shè)計,使用多線程以提高性能可以算是一個困難的任務(wù)。在最壞的情況下,多線程反而會降低(有時很明顯)程序的運行速度。一個計算機科學(xué)專業(yè)的新生就可以告訴你:當多個線程競爭一個共享資源時將會發(fā)生什么。結(jié)果通常不理想。很多情況下多線程都能很好地工作,對于解釋器的實現(xiàn)和內(nèi)核開發(fā)人員來說,不要對  Python 多線程性能有太多抱怨可能是他們***的心愿。

現(xiàn)在該怎么辦呢?慌了嗎?

我們現(xiàn)在能做什么呢?難道作為 Python 開發(fā)人員的我們要放棄使用多線程來實現(xiàn)并行嗎?為什么 GIL  在某一時刻只允許一個線程在運行呢?在并發(fā)訪問時,難道不可以用粒度更細的鎖來保護多個獨立對象?為什么沒有人做過類似的嘗試呢?

這些問題很實用,它們的答案也十分有趣。GIL 為很多對象的訪問提供這保護,比如當前線程狀態(tài)和為垃圾回收而用的堆分配對象。這對 Python  語言來說沒什么奇怪的,它需要使用一個 GIL 。這是該實現(xiàn)的一種產(chǎn)物?,F(xiàn)在也有不使用 GIL 的 Python 解釋器(和編譯器)。但是對于 CPython  來說,從其產(chǎn)生到現(xiàn)在 GIL 就一直在存在了。

那么為什么我們不拋棄 GIL 呢?許多人也許不知道,1999年的時候,Greg Stein 針對 Python 1.5 提交了一個名為“free  threading”的補丁,這個補丁經(jīng)常被提到卻不怎么被人理解。這個補丁就嘗試了將 GIL 完全移除,并用細粒度的鎖來代替。然而,GIL  移除的代價是單線程程序的執(zhí)行速度下降,下降的幅度大概有  40%。使用兩個線程可以讓速度有所提升,但是速度的提升并沒有隨著核數(shù)的增加而線性增長。由于執(zhí)行速度的降低,這一補丁沒有被接受了,并且?guī)缀醣蝗诉z忘。

GIL 讓人頭痛,我們還是想點其他辦法吧

盡管“free threading”這個補丁沒有被接受,但是它還是有啟發(fā)性意義。它證明了一個關(guān)于 Python 解釋器的基本要點:移除 GIL  是非常困難的。比起該補丁發(fā)布的時候,現(xiàn)在的解釋器依賴的全局狀態(tài)變得更多了,這使得移除 GIL 變得更加困難。值得一提的是,也正是因為這個原因,許多人對移除  GIL 變得更感興趣了。困難的問題通常都很有趣。

但是這可能有點被誤導(dǎo)了。我們假設(shè)一下:如果我們有這樣一個神奇的補丁,它其移除了 GIL ,并且沒有使單線程的 Python  代碼性能下降,我們會得到一直想要的東西:一個能并發(fā)使用所有處理器的線程 API?,F(xiàn)在我們已經(jīng)獲得了我們希望的,但這確實是件好事嗎?

基于線程的編程是困難的。當一個人覺得自己了解關(guān)于線程的一切,總會有一些新問題出現(xiàn)。一些非常知名的語言設(shè)計者和研究者站出來反對線程模型,因為在這方面想要得到合理的一致性真的是太難了。就像任何一個寫過多線程應(yīng)用程序的人可以告訴你的一樣,不管是多線程應(yīng)用的開發(fā)還是調(diào)試難度都會是單線程的應(yīng)用的指數(shù)倍。程序員的思維模型往往適應(yīng)順序執(zhí)行模型,恰恰與并行執(zhí)行模型不匹配。GIL  的出現(xiàn)無意中幫助了開發(fā)者免于陷入困境。在使用多線程時仍然需要同步原語,GIL 事實上幫助我們保證不同線程之間的數(shù)據(jù)一致性。

這么說起來 Python 最難的問題似乎有點問錯了問題。Python 專家推薦使用多進程代替多線程是有道理的,而不是想要給 Python  線程實現(xiàn)遮羞。Python  的這種實現(xiàn)方式促使開發(fā)者使用更安全也更直觀的方式實現(xiàn)并發(fā)模型,同時保留使用多線程進行開發(fā),讓開發(fā)者在必要的時候使用。大多數(shù)人可能并不清楚什么是***的并行編程模型。但是大多數(shù)人都清楚多線程的方式并不是***的并行模型。

不要認為 GIL 是一成不變或者毫無道理的。Antoine Pitrou 在 Python 3.2 中實現(xiàn)了一個新的 GIL ,比較顯著地改進的  Python 解釋器。這是1992年以來,針對 GIL 最主要的一次改進。這個改變非常巨大,很難在這里解釋清楚,但是從高層次來看,舊的 GIL 通過對  Python 指令進行計數(shù)來確定何時釋放 GIL。由于 Python 指令和翻譯成的機器指令并非一一對應(yīng)的關(guān)系,這使得單條 Python  指令可能包含大量工作。新的 GIL 用一個固定的超時時間來指示當前的線程釋放鎖。在當前線程持有鎖且第二個線程請求這個鎖的時候,當前線程就會在 5 ms  后被強制釋放這個鎖(這就是說,當前線程每 5 ms 就要檢查其是否需要釋放這個鎖)。在任務(wù)可以執(zhí)行的情況下,這使得預(yù)測線程間的切換變得更容易。

然而,這并不是一個***的改進。對于不同類型任務(wù)執(zhí)行過程中 GIL 的作用的研究,David Beazley 可能是最活躍的一個。除了對 Python  3.2 之前的 GIL 研究最深入,他還研究了這個***的 GIL 實現(xiàn),并且發(fā)現(xiàn)了很多有趣的程序方案:在這些方案中,即使是新的 GIL  實現(xiàn),表現(xiàn)也相當糟糕。他目前仍然通過實踐研究來推動著有關(guān) GIL 的討論,并發(fā)布實踐結(jié)果。

不管人們對 Python 的 GIL 看法如何,它仍然是 Python 語言里最困難的技術(shù)挑戰(zhàn)。想要理解它的實現(xiàn)需要對操作系統(tǒng)設(shè)計、多線程編程、C  語言、解釋器設(shè)計和 CPython 解釋器的實現(xiàn)有著非常透徹的理解。單是這些前提就妨礙了很多開發(fā)者去更徹底地研究 GIL。然而并沒有任何跡象表明 GIL  會在不久之后遠離我們。目前,它將繼續(xù)給那些新接觸 Python 并對解決技術(shù)難題感興趣的人帶來困惑和驚喜。

以上內(nèi)容是基于我目前對 Python 解釋器的研究。我打算寫一些關(guān)于解釋器其它方面的內(nèi)容,但是沒有比 GIL 知名度更高的了。雖然這些技術(shù)細節(jié)來自我對  CPython 代碼庫的徹底研究,但是仍有可能存在不準確的地方。如果你發(fā)現(xiàn)了不準確的內(nèi)容,請及時告知我,我會盡快修正。

到此,相信大家對“Python全局解釋器鎖能做什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI