全局解释器锁(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)