【发布时间】:2014-08-21 08:55:12
【问题描述】:
CPython 线程支持的文档令人沮丧地相互矛盾和稀疏。
一般来说,似乎每个人都同意嵌入 Python 的多线程 C 应用程序必须始终在调用 Python 解释器之前获取 GIL。通常,这是通过以下方式完成的:
PyGILState_STATE s = PyGILState_Ensure();
/* do stuff with Python */
PyGILState_Release(s);
文档非常清楚地说明了这一点:https://docs.python.org/2/c-api/init.html#non-python-created-threads
但是,在实践中,如何让嵌入 Python 的多线程 C 程序真正顺利运行是另一回事。即使您完全按照文档进行操作,似乎也有很多怪癖和惊喜。
例如,似乎在幕后,Python 区分了“主线程”(我猜是调用Py_Initialize 的线程)和其他线程。具体来说,当我尝试获取 GIL 并在“主”线程中运行 Python 代码时,任何尝试都失败了——(至少在 Python 3.x 中),程序中止并显示Fatal Python error: drop_gil: GIL is not locked 消息,即愚蠢,因为 GIL 被锁定了!
例子:
int main()
{
Py_Initialize();
PyEval_InitThreads();
PyEval_ReleaseLock();
assert(PyEval_ThreadsInitialized());
PyGILState_STATE s = PyGILState_Ensure();
const char* command = "x = 5\nfor i in range(0,10): print(x*i)";
PyRun_SimpleString(command);
PyGILState_Release(s);
Py_Finalize();
return 0;
}
这个简单的程序以“GIL 未锁定错误”中止,即使我明确锁定了它。但是,如果我生成另一个线程,并尝试在该线程中获取 GIL,一切正常。
因此,CPython 似乎有一个(未记录的)“主线程”概念,它与 C 产生的辅助线程有所不同。
问题:这是否记录在任何地方?有没有人有任何经验可以阐明获取 GIL 的确切规则是什么,以及是否应该在“主”线程与子线程中对此有任何影响?
PS:另外,我注意到PyEval_ReleaseLock 是deprecated API call,但我还没有看到任何实际可行的替代方案。如果您在调用PyEval_InitThreads 之后不调用PyEval_ReleaseLock,您的程序会立即挂起。但是,newer alternative mentioned in the docs、PyEval_SaveThread 在实践中从未对我有用 - 它会立即出现 seg 错误,至少如果我在“主”线程中调用它的话。
【问题讨论】:
-
这个问题暗示你根本不应该调用 PyEval_ReleaseLock:stackoverflow.com/q/15470367/321772
-
你应该问python-dev。