2011-12-09 61 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

相關問題