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