Python 多線程的單cpu與cpu上的多線程的區(qū)別
你對(duì)Python 多線程有所了解的話。那么你對(duì)python 多線程在單cpu意義上的多線程與多cpu上的多線程有著本質(zhì)的區(qū)別,如果你對(duì)Python 多線程的相關(guān)知識(shí)想有更多的了解,你就可以瀏覽我們的文章。
Python多線程是單cpu意義上的多線程,它和多cpu上的多線程有著本質(zhì)的區(qū)別。
單cpu多線程:并發(fā)
多cpu多線程:并行內(nèi)部包含并發(fā)
在Python 多線程當(dāng)中,存在一個(gè)叫Global Interpreter Lock(GIL)的東西,直譯就是全局解釋器鎖。它的作用在于讓同一時(shí)刻只能有一個(gè)線程對(duì)于python對(duì)象進(jìn)行操作。Python已經(jīng)提供了各種機(jī)制讓我們進(jìn)行多線程同步,為什么又要整這個(gè)GIL呢?這是因?yàn)槌绦騿T控制的同步是對(duì)各個(gè)程序中可見(jiàn)的變量,而GIL同步的是解釋器后臺(tái)的不可見(jiàn)變量,比如為了進(jìn)行垃圾回收而維護(hù)的引用計(jì)數(shù)。如果沒(méi)有GIL,有可能出現(xiàn)由于線程切換導(dǎo)致的對(duì)同一個(gè)對(duì)象釋放兩次的情況。#t#
因此,任何一個(gè)CPython線程如果要執(zhí)行,就必須先獲取這個(gè)GIL。后果?就是在CPython中,本質(zhì)上幾乎是沒(méi)有線程并行的,不論你開(kāi)多少個(gè)線程,同一時(shí)刻只有獲取GIL的那個(gè)線程能夠執(zhí)行。為什么要說(shuō)幾乎呢,這是因?yàn)樘峁┙opython的C庫(kù)中,還是有解決方案的,比如
這段代碼是sleep的代碼,在執(zhí)行sleep之前,通過(guò)一個(gè)宏來(lái)釋放GIL,然后在睡眠結(jié)束執(zhí)行其他代碼前又獲取GIL。其他一下操作,比如IO,也會(huì)有類似的操作,這樣就使得對(duì)于IO密集型的程序,或者是使用C庫(kù)進(jìn)行計(jì)算的程序,還是可以在很大程度上避開(kāi)GIL來(lái)取得線程并行的效果的。但對(duì)于純python代碼的程序,GIL恐怕還是躲不過(guò)去的。
還有一個(gè)問(wèn)題,就是GIL怎么釋放,我們看到在python/C API中提供了宏來(lái)進(jìn)行釋放,那么對(duì)于普通的python語(yǔ)句呢?解釋器會(huì)在執(zhí)行一百條python代碼后強(qiáng)制釋放GIL,這就使得其它線程得以執(zhí)行。
最后需要說(shuō)明的,就是這個(gè)GIL的問(wèn)題是解釋器相關(guān)的,而不是語(yǔ)言相關(guān)的。也就是說(shuō)它只是對(duì)于python語(yǔ)言解釋器的一種實(shí)現(xiàn),并不是語(yǔ)言本身的特性。事實(shí)上,GIL就是解釋器的一個(gè)非常粗粒度的鎖,我們完全可以采用更細(xì)粒度的鎖來(lái)增加并行性,而且Gindo就寫過(guò)一個(gè)patch來(lái)取消GIL,不過(guò)好像最后的結(jié)果是細(xì)粒度鎖導(dǎo)致了單線程程序的性能下降了兩倍,所以最后還是決定優(yōu)先保證單線程程序的性能,繼續(xù)保留GIL。但是python的其他兩個(gè)分支,Jython和IronPython,卻都沒(méi)有GIL的問(wèn)題,從而可以實(shí)現(xiàn)線程的并行。
總結(jié):
通常加鎖也有2種不同的粒度的鎖:
fine-grained(所謂的細(xì)粒度), 那么程序員需要自行地加,解鎖來(lái)保證線程安全
coarse-grained(所謂的粗粒度), 那么語(yǔ)言層面本身維護(hù)著一個(gè)全局的鎖機(jī)制,用來(lái)保證線程安全
Python 多線程 從語(yǔ)言層面本身維護(hù)著一個(gè)全局的鎖機(jī)制,用來(lái)保證線程安全;而java, Jython則是細(xì)粒度的。
所以也就是說(shuō),由于gil的限制,python語(yǔ)言本身是不能夠進(jìn)行并行編程的,但是可以進(jìn)行并發(fā)編程;而java則沒(méi)有g(shù)il意義上的限制,因此java從java7開(kāi)始已經(jīng)開(kāi)始往并行上偏移了。