全局解釋器鎖GIL

解釋一下對GIL的理解?java

  GIL 又叫全局解釋器鎖,首先說一點,Python語言與GIL全局解釋器鎖沒有關係,僅僅是由於歷史緣由,在cpython解釋器中還存在GIL難以移除。GIL是功能與性能權衡後的產物,它有着存在的合理性,也有着難以移除的歷史客觀因素。python

 

爲何存在GIL?編程

  在早期的開發過程當中,由於物理因素限制,從最開始的單核CPU發展爲多核CPU, 想要充分發揮多核CPU的性能須要利用到多線程編程,Python中一樣引入了多線程編程,而多線程編程引入帶來的問題是:線程之間數據的一致性和狀態同步的問題。 而想解決這些問題最好的方法就是加一把鎖,因此就有了GIL全局解釋器鎖這樣一把大鎖。多線程

  隨着愈來愈多的代碼庫開發者接受GIL,然後逐漸大量依賴於這一特性進行開發,到最後發現GIL對多核CPU多線程編程的效率是低效影響的時候,想要移除這一特性,卻發現已經很難了。併發

 

談談GIL的做用?性能

  第一個做用: 當前線程必須須要先獲取GIL,才能進入CPU執行代碼。GIL的存在保障了在同一時刻只能有一個線程獲取GIL,執行代碼。線程

  第二個做用:當遇到IO阻塞時,執行線程會釋放GIL,給其餘線程獲取鎖執行代碼的機會。設計

  

  問題: 若是是CPU密集型,一直佔有CPU,而沒有遇到IO阻塞,是否是其餘線程就沒有機會執行?接口

  其實也並非這樣,在解釋器中會進行週期性的代碼檢測和執行代碼調度,在Python2中使用的是計數器的方式釋放GIL,就是當計數達到必定閥值,當前執行線程就會釋放GIL給其餘線程執行的機會。但會出現的問題就是: 當前執行線程剛釋放了GIL可能又會當即再獲取GIL進行執行。 在Python3中使用的是計時器的方式釋放GIL,就是當前執行線程執行時間達到必定閥值就會釋放GIL,給其餘線程執行的機會。這樣避免了當前執行線程剛釋放GIL又當即獲取的狀況,同時在線程中增長了線程優先級,高優先級的線程能夠迫使執行的線程釋放GIL,進行執行。隊列

 

談談GIL的設計缺陷和影響?

  在早期的開發過程當中,爲了讓各個線程可以平均的利用CPU的執行時間,python中採用的是計數的方式切換執行代碼,就是當計數(執行的線程代碼數)達到必定的閥值,執行線程就會釋放GIL鎖,給其餘線程執行的機會。這一模式在單核CPU中沒有問題,由於不管是其餘哪一個線程被喚醒,都可以成功的獲取GIL進入CPU執行代碼。而在多核CPU中則會有問題,當喚醒其它核心上的線程時候,大多數狀況下老是當前主線程剛剛釋放GIL,又會當即再次獲取GIL進行執行,而其餘被喚醒的線程只能白白等待浪費CPU的執行時間,等到執行時間結束,會進入到待喚醒待調度狀態,再次被喚醒,再次等待,如此惡性循環着。

  GIL的影響有: GIL無疑是一把全局排他鎖,它的存在保證了再同一時刻只能有一個線程獲取GIL進行執行代碼,因此就沒法讓多核CPU多線程實現並行,而想充分發揮多核CPU的最大性能就是實現多任務並行。  下面解釋一下什麼是併發和並行。

  併發: 當任務數大於CPU核心數時,總有一些任務是沒有在執行的,只不過是由於CPU的切換速度很快,讓人感受像是多任務同時在一塊兒執行。

  並行: 當任務數小於或者等於CPU核心數時,每一個任務都有一個對應的核來處理執行,是真正意義上的多任務同時一塊兒執行。

 

如何避免GIL的影響?

  方法一: 更換python解釋器,好比jpython,用java開發的python解釋器。 但由於衆多的庫都是創建在GIL這一特性下開發的,因此更換解釋器不少庫用不了,不划算。

  方法二: 使用多進程代替多線程。 multiprocession庫的開發很大程度上就是爲了彌補threading庫由於GIL特性低效的缺陷,它完整的複製了一份threading裏面的API接口便於遷移管理。 惟一的不一樣就是它是多進程而不是多線程,每個進程都有本身的GIL鎖,不會出現進程直接GIL鎖的競爭。而多線程的時候則會出現釋放GIL多個線程同時爭搶鎖的狀況,這樣會浪費CPU的性能資源。

  但多進程也不是萬良解藥,它的引入同時會增長實現進程間通訊和狀態同步的難度,在多線程中對公共資源進行修改,只須要在線程中gloab 聲明一下就能夠了,多線程之間是共享全局變量的。而在多進程中,則須要使用一個Queue隊列,經過put 或者get來傳遞數據,增長了開發的難度。多進程間是不共享全局變量的。

相關文章
相關標籤/搜索