全域性直譯器鎖(GIL)和阻塞執行緒

關於 Python 的 GIL已經 很多。在處理多執行緒(不要與多程序混淆)應用程式時,它有時會引起混淆。

這是一個例子:

import math
from threading import Thread

def calc_fact(num):
    math.factorial(num)

num = 600000
t = Thread(target=calc_fact, daemon=True, args=[num])
print("About to calculate: {}!".format(num))
t.start()
print("Calculating...")
t.join()
print("Calculated")

你可能希望線上程啟動後立即列印出 Calculating...,我們希望計算在新執行緒中完成! 但實際上,你會看到計算完成後會列印出來。這是因為新執行緒依賴於 C 函式(math.factorial),它將在執行時鎖定 GIL。

有幾種方法可以解決這個問題。第一個是在本機 Python 中實現你的階乘函式。這將允許主執行緒在你進入迴圈時獲取控制權。缺點是,這種解決方案將是一個很大較慢,因為我們沒有使用 C 函式了。

def calc_fact(num):
    """ A slow version of factorial in native Python """
    res = 1
    while num >= 1:
        res = res * num
        num -= 1
    return res

在開始執行之前,你還可以在一段時間內使用 sleep。注意:這實際上不會允許程式中斷 C 函式內發生的計算,但它會允許主執行緒在生成後繼續,這是你可能期望的。

def calc_fact(num):
    sleep(0.001)
    math.factorial(num)