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