9

假设我有一个 C 扩展函数,它做的事情完全独立于 Python 解释器。有什么理由发布 GIL?

例如,是否有任何理由不编写这样的代码(除了诸如可读性和避免微优化之类的问题——这些问题很重要,但与我的问题并不真正相关)?

Py_BEGIN_ALLOW_THREADS
    a = 1 + 1;
Py_END_ALLOW_THREADS

显然,这是一些性能可能无关紧要的琐碎代码。但是有什么性能原因不在这里发布 GIL 吗?还是应该只为更多 CPU 密集型代码发布 GIL?

4

4 回答 4

9

GIL 是一个普通的互斥体。锁定或解锁无竞争互斥体的成本极低,不比更改全局变量的成本高多少。但是,如果您经常锁定和解锁有争议的互斥锁,则互斥锁的成本可能会变得很大。

所以,这通常不是一个好主意:

Py_BEGIN_ALLOW_THREADS
    a = 1 + 1;
Py_END_ALLOW_THREADS

这里发生的事情是您正在解锁一个互斥锁,然后您会立即再次尝试锁定该互斥锁。如果这是两个大代码块之间的中断,那么它会给另一个线程一个运行的机会。但是如果你没有线程粒度的问题,就保持锁。

所以在这种情况下这是一个好主意:

very_long_computation_requires_gil();
Py_BEGIN_ALLOW_THREADS;
a = a + i;
Py_END_ALLOW_THREADS;
very_long_computation_also_requires_gil();

在不了解上下文的情况下做出有根据的猜测确实是不可能的,而且如果不运行测试通常仍然很困难。

于 2011-10-22T00:15:40.717 回答
1

专家们仍在对 GIL 进行调整和测试。

这是关于一个老问题的新想法: inside-look-at-gil-removal-patch

您还可以考虑尝试 Stackless Python(无 GIL)或 PyPy(带有即时编译器的 Python)。

于 2011-11-20T13:43:54.823 回答
1

如果你有一个完全独立于 Python 解释器的 C 扩展函数,那么释放 GIL 通常是个好主意。唯一的缺点是等待 GIL 回来。在 Python 3.2 中,您必须等待至少 1/20 秒。

于 2011-10-22T01:57:52.233 回答
0

有什么理由不发布 GIL?

如果 C 扩展调用不可重入代码,那么如果多个 Python 线程同时调用扩展,您可能会遇到问题。因此,您可能希望避免在此类扩展中释放 GIL 以防止发生这种情况(当然,您可以在 Python 级别或 C 级别创建自己的互斥锁来实现此目的而不会影响其他线程)。

还是应该只为更多 CPU 密集型代码发布 GIL?

释放 GIL 的另一个主要原因是当调用阻塞的 C 扩展(例如套接字上的阻塞读取)以使其他线程能够运行时。这正是 Python 解释器本身在线程中执行阻塞操作时发生的情况。

于 2011-10-31T04:56:54.197 回答