>GIL爲何物
GIL(Global Interpreter Lock),也稱爲全局解釋器,看下官方解釋
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
主要意思爲:
GIL是一個互斥鎖,它防止多個線程同時執行Python字節碼。這個鎖是必要的,主要是因爲CPython的內存管理不是線程安全的
>Python與GIL
Python的GIL只是CPython(Python解釋器)的一個問題,那麼Python又有哪些解釋器,它們也存在和CPython同樣的問題嗎?那麼什麼又是解釋器呢?
什麼是解釋器
我們寫的代碼計算機是如何識別的呢,計算機也擁有和人類相同的思維和語言嗎?顯然不是的;計算機只能識別機器指令語言也就是0和1,那麼我們編寫的程序計算機是如何識別的呢?這就是解釋器的作用了,解釋器將我們編寫的Python代碼翻譯爲機器指令語言,Python解釋器本身也是個程序,它是解釋Python代碼的,叫做解釋器.
Python解釋器有哪些
- CPython: 官方默認版本,使用C語言開發,是Python使用最廣泛的解釋器,有GIL.
- IPython: IPython是基於CPython之上的交互式解釋器,其它方面和CPython相同.
- PyPy: PyPy採用JIT(Just In Time)也就是即時編譯編譯器,對Python代碼執行動態編譯,目的是加快執行速度,有GIL.
- Jython: 運行在Java平臺上的解釋器,把Python代碼編譯爲Java字節碼執行,沒有GIL.
- IronPython: IronPython和Jython類似,只不過IronPython是運行在微軟.Net平臺上的Python解釋器,可以直接把Python代碼編譯成.Net的字節碼,沒有GIL.
>GIL解決了Python什麼問題呢
Python內部對變量或數據對象使用了引用計數器,我們通過計算引用個數,當個數爲0時,變量或者數據對象就被自動釋放
In [1]: import sys
In [2]: count_var = "test1"
In [3]: sys.getrefcount(count_var)
Out[3]: 2
In [4]: add_var = count_var
In [5]: sys.getrefcount(add_var)
Out[5]: 3
這個引用計數器需要保護,當多個線程同時修改這個值時,可能會導致內存泄漏;SO,我們使用鎖來解決這個問題,可有時會添加多個鎖來解決,這就會導致另個問題,死鎖;
爲了避免內存泄漏和死鎖問題,CPython使用了單鎖,即全局解釋器鎖(GIL),即執行Python字節碼都需要獲取GIL,這可以防止死鎖,但它有效地使任何受CPU限制的Python程序都是單線程.
>GIL對多線程Python程序的影響
程序的性能受到計算密集型(CPU)的程序限制和I/O密集型的程序限制影響,那什麼是計算密集型和I/O密集型程序呢?
計算密集型(CPU)
高度使用CPU的程序,例如: 進行數學計算,矩陣運算,搜索,圖像處理等.
I/O密集型
I/0(Input/Output)程序是進行數據傳輸,例如: 文件操作,數據庫,網絡數據等
測試下順序執行單線程和併發執行多線程的效率
- 順序執行單線程(single_thread.py)
import threading
import time
def test_counter():
i = 0
for _ in range(100000000):
i += 1
return True
def main():
start_time = time.time()
for tid in range(2):
t1 = threading.Thread(target=test_counter)
t1.start()
t1.join()
end_time = time.time()
print("Total time:{}".format(end_time-start_time))
if __name__ == "__main__":
main()
執行結果:
Total time: 11.299654722213745
- 併發執行兩個線程(multi_thread.py)
import threading
import time
def test_counter():
i = 0
for _ in range(10000000):
i +=1
# return True
def main():
start_time = time.time()
thread_array = {}
for tid in range(2):
t1 = threading.Thread(target=test_counter())
t1.start()
thread_array[tid] = t1
for tid in range(2):
thread_array[tid].join()
end_time = time.time()
print(end_time-start_time)
print("Total time:{} ".format(end_time-start_time))
if __name__ == '__main__':
main()
執行結果:
Total time:13.7098388671875
GIL對I/O綁定多線程程序的性能影響不大,因爲線程在等待I/O時共享鎖.
GIL對計算型綁定多線程程序有影響,例如: 使用線程處理部分圖像的程序,不僅會因鎖定而成爲單線程,而且還會看到執行時間的增加,這種增加是由鎖的獲取和釋放開銷的結果.
>可以去掉累贅GIL嗎
有大佬試過,只能說結果不盡人意0 .0,等待着吧
>SO,如何處理Python中的GIL
- 計算密集型程序
- 使用多進程(什麼是多進程呢,後續道來)
- 使用其它語言(將計算密集程序放到其它語言中執行)
- 替換解釋器(可以自己嘗試)
- 等大神解決GIL0 .0
- I/O密集型程序
- 使用多線程
- 使用多進程
- 使用多進程+多線程