Python最强大的一点是易于编写C和C ++扩展,以加速处理器密集型代码部分。这些扩展可以避免全局解释器锁定还是受GIL限制?如果没有,那么这种“易于扩展”甚至比我之前意识到的更具杀伤力。我怀疑答案不是简单的是或否,但我不确定,所以我在StackOverflow上问这个问题。
答案 0 :(得分:19)
是的,对C扩展的调用(从Python调用的C例程)仍然受GIL的约束。
但是,您可以手动在C扩展中释放GIL,只要在将控制权返回给Python VM之前小心重新断言它。
有关信息,请查看Py_BEGIN_ALLOW_THREADS
和Py_END_ALLOW_THREADS
宏:http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock
答案 1 :(得分:8)
Python的C / C ++扩展不受GIL的约束。但是,你真的需要知道你在做什么。 From http://docs.python.org/c-api/init.html:
全局解释器锁用于保护指向当前线程状态的指针。释放锁并保存线程状态时,必须在释放锁之前检索当前线程状态指针(因为另一个线程可以立即获取锁并将其自己的线程状态存储在全局变量中)。相反,在获取锁定并恢复线程状态时,必须在存储线程状态指针之前获取锁定。
为什么我要继续这么详细呢?因为当从C创建线程时,它们没有全局解释器锁,也没有针对它们的线程状态数据结构。这些线程必须首先创建一个线程状态数据结构,然后获取锁,最后存储它们的线程状态指针,然后才能开始使用Python / C API。完成后,他们应该重置线程状态指针,释放锁,最后释放他们的线程状态数据结构。
答案 2 :(得分:1)
如果您使用C ++编写扩展,则可以使用RAII轻松,清晰地编写操作GIL的代码。我使用这对RAII结构:
namespace py {
namespace gil {
struct release {
PyThreadState* state;
bool active;
release()
:state(PyEval_SaveThread()), active(true)
{}
~release() { if (active) { restore(); } }
void restore() {
PyEval_RestoreThread(state);
active = false;
}
};
struct ensure {
PyGILState_STATE* state;
bool active;
ensure()
:state(PyGILState_Ensure()), active(true)
{}
~ensure() { if (active) { restore(); } }
void restore() {
PyGILState_Release(state);
active = false;
}
};
}
}
...允许为给定的块切换GIL(以语义方式,对任何上下文管理器Pythonista粉丝来说可能看起来模糊不清):
PyObject* YourPythonExtensionFunction(PyObject* self, PyObject* args) {
Py_SomeCAPICall(…); /// generally, if it starts with Py* it needs the GIL
Py_SomeOtherCall(…); /// ... there are exceptions, see the docs
{
py::gil::release nogil;
std::cout << "Faster and less block-y I/O" << std::endl
<< "can run inside this block -" << std::endl
<< "unimpeded by the GIL";
}
Py_EvenMoreAPICallsForWhichTheGILMustBeInPlace(…);
}
......事实上,我个人也发现扩展Python的难易程度,以及对内部结构和状态的控制程度,这是一个杀手锏。
答案 3 :(得分:0)
查看Cython,它具有与Python类似的语法,但有一些结构,如“cdef”,快速numpy访问函数和“with nogil”语句(完成它所说的)。