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

Why is PyGILState_Release throwing Fatal Python Errors

本文关键字:Python 错误 PyGILState Release 为什么      更新时间:2023-10-16

>已回答

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

BOOST_PYTHON_MODULE(ModuleName)
{
    PyEval_InitThreads();
    ...
}

好的,我已经尝试诊断这个问题几个小时,并倾注了网络上的每一个示例。现在累了,所以我可能会错过一些明显的东西,但这是正在发生的事情:

我正在用提升蟒蛇包装一个库。我正在运行一个 python 脚本,它导入库,构造一些对象,然后从 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<>调用该方法。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没有其他地方被调用,其他地方不应该调用 python。

更新

我读过另一个问题,它建议在模块中导入线程作为运行的替代方案

PyEval_InitThreads();
PyEval_ReleaseLock();

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

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

BOOST_PYTHON_MODULE(ModuleName)
{
    PyEval_InitThreads();
    ...
}