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

时间:2011-12-09 20:27:55

标签: c++ python boost boost-python

ANSWERED

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

BOOST_PYTHON_MODULE(ModuleName)
{
    PyEval_InitThreads();

    ...
}

好的,我已经尝试了几个小时来诊断这个问题,并通过网络上的每个例子进行了灌输。现在累了,所以我可能会遗漏一些明显的东西,但现在正在发生的事情:

我在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 
    }

    ...
}

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

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

# Initialize the library and setup callbacks
...

# Wait until user breaks
while 1:
  pass

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

PyEval_InitThreads();
PyEval_ReleaseLock();

在收到任何回调之前。

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

Fatal Python error: ceval: tstate mix-up
Fatal Python error: This thread state must be current when releasing

Fatal Python error: ceval: orphan tstate
Fatal Python error: This thread state must be current when releasing

看似随意。我在这里疯了,因为我觉得我正确地使用了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();

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

1 个答案:

答案 0 :(得分:9)

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

BOOST_PYTHON_MODULE(ModuleName)
{
    PyEval_InitThreads();

    ...
}