python GIL鎖問題

學習內容來源於《流暢的python第17章內容》,我覺得講的挺清楚的

CPython 解釋器本身就不是線程安全的, 因此有全局解釋器鎖(GIL)確保線程操作安全 ,
所以一次只允許使用一個線程執行 Python 字節碼。 因此, 一個 Python 進程通常不能同時使用多個 CPU 核心。
這是 CPython 解釋器的侷限, 與 Python 語言本身無關。 Jython 和 IronPython 沒有這種限制。
不過, 目前最快的Python 解釋器 PyPy 也有 GIL。

然而, 標準庫中所有執行阻塞型 I/O 操作的函數, 在等待操作系統返回結果時都會釋放 GIL。 這意味着在 Python 語言這個層次上可以使用多線程, 而 I/O 密集型 Python 程序能從中受益: 一個 Python 線程等待網絡響應時, 阻塞型 I/O 函數會釋放 GIL, 再運行一個線程。
Python 標準庫中的所有阻塞型 I/O 函數都會釋放 GIL, 即標準庫中每個使用 C 語言編寫的 I/O 函數都會釋放 GIL, 因此, 當某個線程在等待 I/O 時, Python 調度程序會切換到另一個線程,允許其他線程運行。 time.sleep() 函數也會釋放 GIL。 因此, 儘管有GIL, Python 線程還是能在 I/O 密集型應用中發揮作用。

也就是說 python多線程 :
在I/O密集型時,是可以使用的,此時無GIL鎖。
在CPU密集型時,只有一個線程運行,此時只能使用一個CPU ,有GIL所鎖

在 CPU 密集型作業中使用 concurrent.futures 模塊中的 ProcessPoolExecutor可以輕鬆繞開 GIL
使用 ProcessPoolExecutor 類把工作分配給多個Python 進程處理。
因此, 如果需要做 CPU 密集型處理, 使用這個模塊能繞開 GIL, 利用所有可用的 CPU 核心
如果使用 Python 處理 CPU 密集型工作, 應該試試PyPy解釋器

在I/O 密集型作業中使用concurrent.futures 模塊中的ThreadPoolExecutor 可以進行多線程任務執行
Python 線程特別適合 I/O 密集型應用, concurrent.futures 模塊大大簡化了某些使用場景下 Python 線程的用法。

如果concurrent.futures包 並不能滿足你的簡單任務需求,我們可以使用multiprocessing,threading 利用裏面的各種組件來完成我們複雜的需求

其實ProcessPoolExecutor/ThreadPoolExecutor 是封裝了multiprocessing/threading 裏面的各種組件,使我們能夠方便的使用,的確很方便。也就是我們通常所說的concurrent.futures併發包,它們的成員方法接口都一樣,方便我們不需要注意太多內部實現細節,即可實現多進程、多線程任務的併發操作。

Executor.map 函數易於使用, 不過有個特性可能有用, 也可能沒用,
具體情況取決於需求: 這個函數返回結果的順序與調用開始的順序一
致。 

executor.submit 和 futures.as_completed 這個組合比executor.map 更靈活,
因爲 submit 方法能處理不同的可調用對象和參數,
而 executor.map 只能處理參數不同的同一個可調用對象。
此外, 傳給 futures.as_completed 函數的期物集合可以來自多個 Executor 實例,
例如一些由 ThreadPoolExecutor 實例創建, 另一些由 ProcessPoolExecutor 實例創建。

併發是指一次處理多件事。
並行是指一次做多件事。
二者不同, 但是有聯繫。
一個關於結構, 一個關於執行。
併發用於制定方案, 用來解決可能(但未必) 並行的問題

真正的並行需要多個核心
現代的筆記本電腦有4 個 CPU 核心, 但是
通常不經意間就有超過 100 個進程同時運行。 因此, 實際上大多數過程
都是併發處理的, 而不是並行處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章