11.1 Global Interpreter Lock
Global Interpreter Lock (GIL)
是在
CPython 解釋器中的一個機制,確保僅有一個執行緒
能在任何時候執行 Python 的 bytecode。GIL
阻止
執行緒的平行執行,這可能會對多執行緒程式的
效能產生負面影響,特別是在多核心處理器上。
為什麼 GIL
會存在
簡化記憶體管理:GIL
簡化了
記憶體管理和垃圾回收,使 Python 更容易實現。
執行緒安全:GIL
防止競態條件,
使得程式的執行對執行緒是安全的,無需使用鎖。
GIL
帶來的問題
受限的效能:多執行緒的程式進行計算密集的
任務時,無法充分利用多核心處理器的優勢,由於
GIL
的限制。
效能扭曲:使用大量執行緒執行任務的程式可能會 因為執行緒間的 context switching 而導致效能下降。
目前有四個主要方法來「繞過 GIL」:
11.2 使用多重處理 (multiprocessing)
模組 multiprocessing
允許創建
平行執行且不受 GIL
限制的進程,因為每個進程都有自己獨立的 Python 解釋器和
記憶體。
範例:
import multiprocessing
def worker(num):
print(f'Worker: {num}')
def main():
processes = []
for i in range(5):
p = multiprocessing.Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
main()
11.3 非同步編程
使用 asyncio
的非同步編程允許在不
阻塞主線程的情況下平行執行任務。雖然這不能直接繞過 GIL
,
但它允許在等待的時間內有效地執行其他任務。
範例:
import asyncio
async def main():
await asyncio.sleep(1)
print('Hello')
asyncio.run(main())
11.4 使用自主管理執行緒的函式庫
一些函式庫,例如 NumPy 和 SciPy,是用 C 編寫的並 使用自主管理執行緒的機制,使其能夠繞過 GIL 並有效 利用多核心處理器進行計算。
這其實是 Python 成功的原因之一,無論其本身有多慢。所有複雜的計算都用 C/C++ 語言重寫並 在處理器的所有核心甚至圖形卡的所有核心上執行。在現代圖形卡上有數千個核心。
這樣一來,無論語言本身快或慢都不再重要,因為所有資源密集的計算都是由外部函式庫進行的 或者完全放在遠端的數據中心。
11.5 在 Python 解釋器外執行計算
使用在 C/C++ 或其他語言上的擴展來執行計算,然後返回 結果。這允許在計算密集的運行期間避免被 GIL 阻塞。
使用 Cython 的範例:
# example.pyx
def compute(int n):
cdef int i, result = 0
for i in range(n):
result += i
return result
編譯和使用:
cythonize -i example.pyx
import example
print(example.compute(1000000))
GO TO FULL VERSION