python全局解释锁_Python GIL 全局解释性锁介绍
什么是GIL
GIL (Global Interpreter Lock),全局解釋性鎖,它上鎖的對象是解釋器,而Python代碼的運(yùn)行需要解釋器進(jìn)行解釋成字節(jié)碼并提供虛擬機(jī)運(yùn)行,這么大粒度的鎖意味著,一個Python進(jìn)程內(nèi)的線程只有先獲得GIL,才能得到代碼執(zhí)行的機(jī)會,這個鎖使得Python進(jìn)程的多線程無法利用多核cpu帶來性能提升。
但需要明確的一點(diǎn)是,GIL并不是Python的特性,而是CPython(最常用的Python解釋器)的特性。這意味著我們可以使用其他的Python解釋器從而繞過GIL的困擾,如JPython就沒有GIL。但由于CPython是大部分環(huán)境下默認(rèn)的Python解釋器,而且Python社區(qū)大部分第三方庫也依賴GIL這個特性提供的線程安全,由于這樣的歷史路徑依賴,我們對GIL是欲罷不能。
看見GIL
接下來我們通過幾個簡單的例子來看看GIL對多核利用的影響
運(yùn)行如下代碼看看CPU的占用率
123456
def dead_loop():
while True:
pass
if __name__ == "__main__":
dead_loop()
測試的機(jī)器是個人 macbook pro,4個物理CPU,劃分為8個邏輯CPU;可以看到該程序的確是把單核的占用跑滿了;接著我們多開一個線程一起跑dead_loop,線程是CPU調(diào)度的基本單位,按道理兩個線程應(yīng)該并行運(yùn)行,CPU占用應(yīng)該提高一倍;
12345678910111213
import threading
def dead_loop():
while True:
pass
if __name__ == "__main__":
t = threading.Thread(target=dead_loop)
t.start()
dead_loop()
t.join()
如圖,確實(shí)是運(yùn)行了兩個線程,但是只有一個線程是激活的,只跑滿一個核,CPU的占用率依舊是 1?8 左右 【因?yàn)橛衅渌脩舫绦?#xff0c;因此略高于 1?8 】。
作為對比,我們使用Golang跑兩個線程看看。
123456789101112131415
package main
func main(){
ch := make(chan int, 0)
k := 1
for i:=0; i<2; i++{
go func() {
for ; k>0 ; {
}
}()
}
}
可以看到,Golang的確按照預(yù)期的那樣,兩個線程在死循環(huán)運(yùn)行,CPU占用率達(dá)到總的 1?4 左右。
從以上的示例我們可以發(fā)現(xiàn),GIL確實(shí)限制了Python進(jìn)程的多線程對多核CPU的利用。
怎么辦
使用其他解釋器
GIL只是CPython的產(chǎn)物,像JPython和IronPython這樣的解釋器由于實(shí)現(xiàn)語言的特性,它們不需要GIL的幫助,但是由于用來Java/C#用于解釋器的實(shí)現(xiàn),它們也失去了利用社區(qū)眾多C語言模塊有用特性的機(jī)會。【Done is better than perfect】
用multiprocessing替代Thread
multiprocessing庫的出現(xiàn)很大程度上是為了彌補(bǔ)thread庫因?yàn)镚IL而低效的缺陷,它使用了多進(jìn)程而不是多線程,而每個進(jìn)程有自己獨(dú)立的GIL,因此不會出現(xiàn)進(jìn)程間的GIL爭搶。
但是multiprocessing也有其他麻煩,比如本來的多線程的同步和通信機(jī)制在多進(jìn)程下就用不了了,拿計(jì)數(shù)器來舉例子,如果要多個線程同時累加一個變量,對于thread來說,聲明一個global變量,加個訪問鎖即可搞定;但是由于進(jìn)程有自己獨(dú)立的地址空間,無法直接訪問彼此的變量數(shù)據(jù),因此這個共享數(shù)據(jù)就必須從進(jìn)程里提出到更高層的存儲中,苦呀。
看看多進(jìn)程的CPU占用
12345678910111213
import multiprocessing
def dead_loop():
while True:
pass
if __name__ == "__main__":
p = multiprocessing.Process(target=dead_loop)
p.start()
dead_loop()
p.join()
開了兩個進(jìn)程,俱跑滿了單核
社區(qū)的努力
Python社區(qū)也一直在努力地改進(jìn)GIL,甚至嘗試去除GIL
將切換粒度從基于opcode計(jì)數(shù)改成基于時間片計(jì)數(shù)
避免最近一次釋放GIL鎖的線程再次被立即調(diào)度
新增線程優(yōu)先級功能(高優(yōu)先級可以迫使其他線程釋放所持有的GIL鎖)
總結(jié)
Python GIL是功能與性能之間權(quán)衡后的產(chǎn)物,雖然它的存在導(dǎo)致Python單進(jìn)程的CPU密集型多線程形同虛設(shè),但它有其存在的合理性【簡單有用地實(shí)現(xiàn)線程安全】,也有其較難改變的客觀因素【歷史路徑依賴】。
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的python全局解释锁_Python GIL 全局解释性锁介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uml识别事件表格_LOPA分析:使能条
- 下一篇: 12日疯人认证百度云_百度云智峰会12月