为什么PyGILState_Release会抛出致命的Python错误

Dan*_*usa 8 c++ python boost boost-python

ANSWERED

好的,我解决了这个问题.它是如何初始化线程状态的.您根本不需要使用ReleaseLock.只需将InitThreads调用添加到模块定义中:

BOOST_PYTHON_MODULE(ModuleName)
{
    PyEval_InitThreads();

    ...
}
Run Code Online (Sandbox Code Playgroud)

好吧,我已经尝试了几个小时来诊断这个问题并且倾注了网络上的每个例子.现在累了所以我可能会遗漏一些明显的东西,但这里发生了什么:

我在boost python中包装一个库.我正在运行一个python脚本,它导入lib,构造一些对象,然后从c ++接收回调到python的回调.在我调用任何python函数之前,我尝试获取全局解释器锁.以下是一些示例代码:

class ScopedGILRelease
{
public:
   inline ScopedGILRelease()
   {
      d_gstate = PyGILState_Ensure();
   }

   inline ~ScopedGILRelease()
   {
      PyGILState_Release(d_gstate);
   }

private:
   PyGILState_STATE  d_gstate;
};

class PyTarget : public DingoClient::ClientRequest::Target, public wrapper<DingoClient::ClientRequest::Target>
{
  public:
    PyTarget(PyObject* self_) : self(self_) {}
    ~PyTarget() {
      ScopedGILRelease gil_lock;
    }
    PyObject* self;

    void onData(const boost::shared_ptr<Datum>::P & data, const void * closure)
    {
       ScopedGILRelease gil_lock;
       // invoke call_method to python 
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

目标对象上的onData方法由库调用为回调.在python中,我们从PyTarget继承并实现另一个方法.然后我们使用call_method <>来调用该方法.gil_lock获取锁,并通过RIAA保证获取的线程状态始终是一个版本,并且实际上它总是在超出范围时释放.

但是当我在一个试图在这个函数上获得大量回调的脚本中运行它时,它总是会出现段错误.脚本看起来像这样:

# Initialize the library and setup callbacks
...

# Wait until user breaks
while 1:
  pass
Run Code Online (Sandbox Code Playgroud)

此外,python脚本始终构造一个运行的对象:

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

在收到任何回调之前.

我把代码减少到了我甚至没有在onData中调用python的地方,我只是获取了锁.在发布时,它总是崩溃:

Fatal Python error: ceval: tstate mix-up
Fatal Python error: This thread state must be current when releasing
Run Code Online (Sandbox Code Playgroud)

要么

Fatal Python error: ceval: orphan tstate
Fatal Python error: This thread state must be current when releasing
Run Code Online (Sandbox Code Playgroud)

它似乎是随机的.我在这里疯了,因为我觉得我正在使用GIL锁定,但它似乎根本不起作用.

其他注意事项: 只有一个线程调用Target对象的onData方法.

当我使用time.sleep()调用调用python模块中的while循环时,它似乎允许脚本运行更长时间,但最终脚本会出现类似问题的段错误.它持续的时间量与time.sleep(即time.sleep(10)的运行时间长于time.sleep(0.01)成正比.这让我想到了一些脚本在未经我许可的情况下如何重新获取GIL .

PyGILState_Release和PyGILState_Ensure在我的代码中的其他地方被调用no,no else应该调用python.

更新

我已经阅读了另一个问题,它建议模块中的导入线程作为运行的替代方法

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

但是,当我在模块之前导入线程并从boost python包装器中删除上面两行时,它似乎不起作用.

Dan*_*usa 9

好的,我解决了这个问题.它是如何初始化线程状态的.您根本不需要使用ReleaseLock.只需将InitThreads调用添加到模块定义中:

BOOST_PYTHON_MODULE(ModuleName)
{
    PyEval_InitThreads();

    ...
}
Run Code Online (Sandbox Code Playgroud)