在多线程C应用程序中嵌入python

时间:2012-05-16 19:43:58

标签: python c multithreading gil python-embedding

我在多线程C应用程序中嵌入了python解释器,我对使用哪些API来确保线程安全感到有点困惑。

从我收集到的内容中,嵌入python时,在调用任何其他Python C API调用之前,由嵌入器来处理GIL锁。这是通过以下功能完成的:

gstate = PyGILState_Ensure();
// do some python api calls, run python scripts
PyGILState_Release(gstate);

但仅此一点似乎还不够。我仍然有随机崩溃,因为它似乎没有为Python API提供互斥。

在阅读了更多文档后,我还补充说:

PyEval_InitThreads();

在致电Py_IsInitialized()之后,但这就是令人困惑的部分。文档说明了这个函数:

  

初始化并获取全局解释器锁

这表明当此函数返回时,GIL应该被锁定并且应该以某种方式解锁。但实际上这似乎并不是必需的。有了这条线,我的多线程工作完美,并且PyGILState_Ensure/Release功能维持了互斥 当我在PyEval_ReleaseLock()之后尝试添加PyEval_ReleaseLock()时,该应用会在随后的PyImport_ExecCodeModule()调用中很快被锁定。

那我在这里错过了什么?

3 个答案:

答案 0 :(得分:7)

我有完全相同的问题,现在可以在PyEval_SaveThread()之后立即使用PyEval_InitThreads()来解决,如上所述。但是,我的实际问题是我在PyEval_InitThreads()之后使用PyInitialise(),然后在从不同的后续本机线程调用时导致PyGILState_Ensure()阻塞。总之,这就是我现在所做的:

  1. 有全局变量:

    static int gil_init = 0; 
    
  2. 从主线程加载本机C扩展并启动Python解释器:

    Py_Initialize() 
    
  3. 我的应用程序从多个其他线程同时对Python / C API进行大量调用:

    if (!gil_init) {
        gil_init = 1;
        PyEval_InitThreads();
        PyEval_SaveThread();
    }
    state = PyGILState_Ensure();
    // Call Python/C API functions...    
    PyGILState_Release(state);
    
  4. 从主线程停止Python解释器

    Py_Finalize()
    
  5. 我尝试过的所有其他解决方案都会导致使用PyGILState_Ensure()的随机Python sigfaults或死锁/阻塞。

    Python文档确实应该更清楚,至少为嵌入和扩展用例提供了一个示例。

答案 1 :(得分:4)

最终我明白了。
之后

PyEval_InitThreads();

您需要致电

PyEval_SaveThread();

正确释放主线程的GIL。

答案 2 :(得分:-1)

让多线程C应用程序尝试从多个线程与单个CPython实例的多个Python线程进行通信对我来说是冒险的。

只要只有一个C线程与Python通信,即使Python应用程序是多线程的,也不必担心锁定。 如果你需要多个python线程,你可以用这种方式设置应用程序,并让多个C线程通过队列与那个将它们运行到多个Python线程的单个C线程进行通信。

可能对您有用的另一种方法是为每个需要它的C线程提供一个CPython实例(当然,Python程序之间的通信应该通过C程序)。

另一种替代方案可能是Stackless Python解释器。这消除了GIL,但我不确定你遇到其他问题将它绑定到多个线程。 stackless是我(单线程)C应用程序的替代品。