我正在爲PyAudio實施異步音頻播放。後端Portaudio通過創建自己的線程並在需要/有新音頻數據時調用C回調函數來實現異步回放。每當調用C回調函數時,我都會調用之前註冊的Python函數,用戶必須提供音頻數據。爲什麼在這種情況下PyGILState_Release(...)段錯誤?
由於對Python的調用發生在非Python創建的線程中,因此the documentation說我必須在調用Python之前調用PyGILState_Ensure()
,之後調用PyGILState_Release()
。它大致看起來像這樣:
int stream_callback(const void *in, void* out, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData)
{
PyGILState_STATE gstate = PyGILState_Ensure();
/* create some python variables, as used below… */
py_result = PyObject_CallFunctionObjArgs(py_callback,
py_frameCount,
py_inTime,
py_curTime,
py_outTime,
py_inputData,
NULL);
/* evaluate py_result, do some audio stuff… */
PyGILState_Release(gstate);
return returnVal;
}
哪些segfaults在PyGILState_Release(gstate)
。該回調函數經常被調用非常。就像每秒幾百到幾千次一樣。 gstate
是一個32位變量,有時設置爲1
,有時設置爲0
,PyGILState_Ensure()
。它只在設置爲1
時崩潰。通常,將會有一個1
,然後是兩個到四個0
。
這種感覺就像這樣PyGILState_Release(…)
需要比實際返回時間長一些,因此在運行時或者類似的情況下調用。
崩潰時,堆棧跟蹤看起來是這樣的:
#0 0x00007fff88c287b7 in pthread_mutex_lock()
#1 0x00000001001009a6 in PyThread_release_lock()
#2 0x00000001002efc82 in stream_callback (in=0x1014a4670, out=0x1014a4670, frameCount=4316612208, timeInfo=0x1014a4850, statusFlags=4297757032, userData=0x38) at _portaudiomodule.c:1554
#3 0x00000001004e3710 in AdaptingOutputOnlyProcess()
#4 0x00000001004e454b in PaUtil_EndBufferProcessing()
#5 0x00000001004e9665 in AudioIOProc()
#6 0x00000001013485d0 in dyld_stub_strlen()
#7 0x0000000101348194 in dyld_stub_strlen()
#8 0x0000000101346523 in dyld_stub_strlen()
#9 0x0000000101345870 in dyld_stub_strlen()
#10 0x000000010134aceb in AUGenericOutputEntry()
#11 0x00007fff88aa132d in HP_IOProc::Call()
#12 0x00007fff88aa10ff in IOA_Device::CallIOProcs()
#13 0x00007fff88aa0f35 in HP_IOThread::PerformIO()
#14 0x00007fff88a9ef44 in HP_IOThread::WorkLoop()
#15 0x00007fff88a9e817 in HP_IOThread::ThreadEntry()
#16 0x00007fff88a9e745 in CAPThread::Entry()
#17 0x00007fff88c5c536 in _pthread_start()
#18 0x00007fff88c5c3e9 in thread_start()
這是否有道理的人?
您鏈接的問題與PyEval_ReleaseLock有關,永遠不會是正確的調用方式(因爲它已被棄用),所以這不太可能是您的問題。你可以在調試器中精確地看到PyGILState_Release中發生段錯誤的位置嗎?gstate值爲1表示調用來自非Python線程,所以臨時線程狀態實際上正在被銷燬,並且可能會有相當多的事情發生。 – ncoghlan 2011-02-28 13:22:56
另外,你鏈接到哪個版本? (GIL代碼在3.2中改變了一些,所以它使我看到的版本有所不同) – ncoghlan 2011-02-28 13:38:59
我正在使用Python 2.7.1 OSX – bastibe 2011-02-28 13:55:16