2011-12-09 49 views
6

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

好吧,我解决了这个问题。它的一切就是如何初始化线程状态。你根本不需要使用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 <>来调用该方法。 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包装器中删除上述两行。

+1

很高兴您知道了。那个人也把我带回来了,回来的时候。仅供参考,您可以发布自己的问题的答案。这将让喜欢你的解决方案的人可以投票。 –

+0

谢谢,相对较新的堆栈溢出,并仍然得到它的挂钩。将张贴。 –

回答

5

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

BOOST_PYTHON_MODULE(ModuleName) 
{ 
    PyEval_InitThreads(); 

    ... 
} 
+0

感谢兄弟。帮助我。 – genjix

相关问题