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

sho*_*osh 17 c python multithreading python-embedding gil

我在一个多线程的C应用程序中嵌入python解释器,我有点困惑的是我应该使用什么API来确保线程安全.

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

gstate = PyGILState_Ensure();
// do some python api calls, run python scripts
PyGILState_Release(gstate);
Run Code Online (Sandbox Code Playgroud)

但仅凭这一点似乎还不够.我仍然有随机崩溃,因为它似乎不提供Python API的互斥.

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

PyEval_InitThreads();
Run Code Online (Sandbox Code Playgroud)

在电话会议结束后,Py_IsInitialized()这就是令人困惑的部分.文档声明这个函数:

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

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

那我在这里错过了什么?

for*_*man 7

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

  1. 有全局变量:

    static int gil_init = 0; 
    
    Run Code Online (Sandbox Code Playgroud)
  2. 从主线程加载本机C扩展并启动Python解释器:

    Py_Initialize() 
    
    Run Code Online (Sandbox Code Playgroud)
  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);
    
    Run Code Online (Sandbox Code Playgroud)
  4. 从主线程停止Python解释器

    Py_Finalize()
    
    Run Code Online (Sandbox Code Playgroud)

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

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


sho*_*osh 3

最终我想通了。

PyEval_InitThreads();
Run Code Online (Sandbox Code Playgroud)

你需要打电话

PyEval_SaveThread();
Run Code Online (Sandbox Code Playgroud)

同时适当释放主线程的GIL。

  • 事实上,“PyEval_SaveThread()”必须由调用“PyEval_InitThreads()”的线程调用,否则当线程尝试调用“PyGILState_Ensure()”时会发生死锁(因为 GIL 不可检索)。`PyEval_RestoreThread()` 最终应该由调用 `PyEval_SaveThread()` 的同一线程调用,但此时,重要的是所有可能调用 `PyGILState_Ensure()` 的线程都已完成,否则可能会发生死锁,因为只是同样的原因。 (2认同)