GIL(全局解析器鎖)
GIL,全局解析器鎖,只對多線程有影響。
如果沒有GIL,多線程會同時調用全局資源,全局資源會因多個線程同時調用而造成數據錯誤。
因此每個線程在執行過程中都需要先獲取GIL,保證同一時刻只有一個線程在執行代碼,這樣纔不會造成數據錯誤。
多線程並不是真正“多線程”的例子
- 單進程下的CPU利用率
下面代碼保存爲single_thread.py
,同時運行兩個single_thread.py
,兩個程序各佔滿兩個CPU核心,CPU利用率爲100%。
# 主進程死循環,佔用整個CPU
def single_thread():
while True:
pass
test()
- 多線程下的CPU利用率
下面代碼保存爲multithreading.py
,運行後發現,單進程內雙線程即使同時死循環,兩個核心中的CPU資源利用率都僅爲50%。可以認爲實際上只有一個核佔滿資源,和單線程沒區別。
import threading
# 子進程死循環
def test_thread1():
while True:
pass
t1 = threading.Thread(target=test_thread1)
t1.start()
# 主進程死循環
def test_thread2():
while True:
pass
test_thread2()
- 多進程下的CPU利用率
下面代碼保存爲multiprocessing.py
,運行後發現CPU中兩個核心的資源利用率爲100%。
import multiprocessing
# 子進程
def test_multiprocessing1():
while True:
pass
m1 = multiprocessing.Process(target=test_multiprocessing1)
m1.start()
# 主進程
def test_multiprocessing2():
while True:
pass
test_multiprocessing2()
總結:多線程在死循環的程序中也不會導致CPU滿資源的原因是GIL。全局解析器鎖GIL的存在會限制多線程在運行中,實際僅有一個線程在執行任務。
- GIL不是python語言的問題,是python解析器的問題,cpython(C語言寫的解析器)中存在這樣的問題;
- 因此如果用的是cpython解析器,想要發揮多核性能,應該使用多進程;如果是其他解析器,多線程也能發揮多核性能;
- 爬蟲程序中,多線程比單線程性能用提升,因爲在遇到IO阻塞會自動釋放GIL鎖。
計算密集型程序和IO密集型程序
計算密集型
計算密集型程序,多用於計算和分析等領域的程序,GIL在這種程序中是個累贅,多線程等於單線程;多進程沒GIL的影響,因此多進程適合計算密集型程序
IO密集型
IO,input和output輸入輸出類型,程序中常出現阻塞,計時等需要耗時的情況,GIL能在線程阻塞時自動釋放GIL鎖,其他線程就能執行代碼,形成多線程併發的假象;
總結:多線程適用於IO密集型程序
用C語言解決GIL鎖問題
多線程任務用C語言編寫,通過在python代碼中導入C語言文件解決多線程中GIL的問題
- 下面代碼保存爲
deadloop.c
文件
void DeadLoop(){
while(1){
;
}
}
- 通過
gcc 文件名.c -shared -o lib文件名.so
編譯後得到libdeadloop.so
動態庫文件 - 將下面代碼保存爲
solution.py
文件,文件中加載了libdeadloop.so
動態庫中的DeadLoop()
函數作爲子線程
from ctypes import *
import threading
# 加載動態庫libdeadloop
lib = cdll.LoadLibrary("./libdeadloop.so")
# 創建一個子線程,加載動態庫中的DeadLoop()函數,此函數是一個死循環
t = threading.Thread(target=lib.DeadLoop)
t.start()
# 主線程
def multi_threading2():
while True:
pass
multi_threading2()
總結:此時運行
solution.py
文件,多線程中爲兩個死循環,佔用的CPU資源比爲100%,證明解決了GIL鎖線程的問題