python全局解釋器GIL鎖機制詳解
一、GIL全局解釋器鎖
1、GIL鎖不是python的特點。而是cpython的特點。
2、在cpython解釋器中,GIL是一把互斥鎖,用來保證進程中同一個時刻只有一個線程在執(zhí)行。
3、在沒有GIL鎖的情況下,有可能多線程在執(zhí)行一個代碼的同時,垃圾回收機制對所執(zhí)行代碼的變量直接進行回收,其他的線程再使用該變量時會導致運行錯誤。
二、為什么會有GIL鎖?
python使用引用計數(shù)為主,標記清楚和隔代回收為輔來進行內(nèi)存管理。所有python腳本中創(chuàng)建的對象,都會配備一個引用計數(shù),來記錄有多少個指針來指向它。當對象的引用技術為0時,會自動釋放其所占用的內(nèi)存。
假設有2個python線程同時引用一個數(shù)據(jù)(a=100,引用計數(shù)為1),
2個線程都會去操作該數(shù)據(jù),由于多線程對同一個資源的競爭,實際上引用計數(shù)為3,
但是由于沒有GIL鎖,導致引用計數(shù)只增加1(引用計數(shù)為2)
這造成的后果是,當?shù)?個線程結束時,會把引用計數(shù)減少為1;當?shù)?個線程結束時,會把引用計數(shù)減少為0;
當下一個線程再次視圖訪問這個數(shù)據(jù)時,就無法找到有效的內(nèi)存了**
三、多線程無法利用多核優(yōu)勢?
由于GIL鎖的存在,即使是多個線程處理任務,但是最終只有一個線程在工作,那么是不是多線程真的一點用處都沒有了呢?
對于需要執(zhí)行的任務來說,分為兩種:計算密集型、IO 密集型
假如一個計算密集型的任務需要10s的執(zhí)行時間,總共有4個這樣的任務
在 4核及以上的情況下:
多進程:需要開啟 4 個進程,但是 4 個 CPU 并行,最終只需要消耗 10s 多一點的時間。
多線程:只需要開1 個進程,這個進程開啟 4 個線程,開啟線程所消耗的資源很少,但是由于最終執(zhí)行是只有一個 CPU 可以工作,所以最終消耗 40s 多的時間。
假如是多個 IO密集型 的任務
CPU 大多數(shù)時間是處于閑置狀態(tài),頻繁的切換
多進程:進程進行切換需要消耗大量資源
多線程:線程進行切換并不需要消耗大量資源
計算密集型和IO密集型
計算密集型:要進行大量的數(shù)值計算,例如進行上億的數(shù)字計算、計算圓周率、對視頻進行高清解碼等等。這種計算密集型任務雖然也可以用多任務完成,但是花費的主要時間在任務切換的時間,此時CPU執(zhí)行任務的效率比較低。
IO密集型:涉及到網(wǎng)絡請求(time.sleep())、磁盤IO的任務都是IO密集型任務,這類任務的特點是CPU消耗很少,任務的大部分時間都在等待IO操作完成(因為IO的速度遠遠低于CPU和內(nèi)存的速度)。對于IO密集型任務,任務越多,CPU效率越高,但也有一個限度。
計算密集型——采用多進程
執(zhí)行時間為: 4.062887668609619
from multiprocessing import Process import time def func1(): sum=0 for i in range(100000000): sum+=1 print(sum) if __name__ == '__main__': now=time.time() l=[] for i in range(10): p=Process(target=func1) p.start() l.append(p) for p in l: p.join() end=time.time() print('執(zhí)行時間為:',end-now)
計算密集型——采用多線程
執(zhí)行時間為: 27.6159188747406
from threading import Thread import time def func1(): sum=0 for i in range(100000000): sum+=1 print(sum) if __name__ == '__main__': now=time.time() l=[] for i in range(10): p=Thread(target=func1) p.start() l.append(p) for p in l: p.join() end=time.time() print('執(zhí)行時間為:',end-now)
IO密集型——采用多進程
執(zhí)行時間為: 5.388434886932373
from multiprocessing import Process import time def func1(): time.sleep(2) if __name__ == '__main__': now=time.time() l=[] for i in range(100): p=Process(target=func1) p.start() l.append(p) for p in l: p.join() end=time.time() print('執(zhí)行時間為:',end-now)
IO密集型——采用多線程
執(zhí)行時間為: 2.0174973011016846
from threading import Thread import time def func1(): time.sleep(2) if __name__ == '__main__': now=time.time() l=[] for i in range(100): p=Thread(target=func1) p.start() l.append(p) for p in l: p.join() end=time.time() print('執(zhí)行時間為:',end-now)
四、總結
對于IO密集型應用,即便有GIL存在,由于IO操作會導致GIL釋放,其他線程能夠獲得執(zhí)行權限。由于多線程的通訊成本低于多進程,因此偏向使用多線程。
對于計算密集型應用,由于CPU一直處于被占用狀態(tài),GIL鎖直到規(guī)定時間才會釋放,然后才會切換狀態(tài),導致多線程處于絕對的劣勢,此時可以采用多進程+協(xié)程。
到此這篇關于python全局解釋器GIL鎖機制的文章就介紹到這了,更多相關python GIL鎖詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
利用Python計算質(zhì)數(shù)與完全數(shù)的方法實例
這篇文章主要介紹了利用Python計算質(zhì)數(shù)與完全數(shù)的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03關于keras中keras.layers.merge的用法說明
這篇文章主要介紹了關于keras中keras.layers.merge的用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05python使用rstrip函數(shù)刪除字符串末位字符
rstrip函數(shù)用于刪除字符串末位指定字符,默認為空白符,這篇文章主要介紹了python使用rstrip函數(shù)刪除字符串末位字符的方法,需要的朋友可以參考下2023-04-04python測試開發(fā)django之使用supervisord?后臺啟動celery?服務(worker/beat)
Supervisor是用Python開發(fā)的一個client/server服務,是Linux/Unix系統(tǒng)下的一個進程管理工具,不支持Windows系統(tǒng),這篇文章主要介紹了python測試開發(fā)django之使用supervisord?后臺啟動celery?服務(worker/beat),需要的朋友可以參考下2022-07-07Python+PyQt5+MySQL實現(xiàn)天氣管理系統(tǒng)
這篇文章主要為大家詳細介紹了Python+PyQt5+MySQL實現(xiàn)天氣管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-06-06