我正致力于为PyAudio实现异步音频播放。后端Portaudio通过创建自己的线程并在需要/拥有新的音频数据时调用C回调函数来实现异步回放。无论什么时候调用C-callback函数,我都会调用以前注册的Python函数,用户必须提供音频数据。
由于这种对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;
}
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 ()
这对任何人都有意义吗?
答案 0 :(得分:6)
我有完全相同的问题。修复是在发生任何回调之前在主线程上调用PyEval_InitThreads()
。
我认为其原因如下。当Python解释器首次启动时,它避免初始化GIL,因为大多数Python程序都是单线程的,并且GIL的存在会导致一些小的性能损失。因此,如果没有初始化GIL,PyGILState_Ensure()
和PyGILState_Release()
会处理未初始化的数据,导致整个地方发生奇怪的崩溃。
通过调用PyEval_InitThreads()
,GIL已初始化,PyGILState_Ensure()
和PyGILState_Release()
正常工作。如果GIL已经初始化PyEval_InitThreads()
什么都不做,那么一遍又一遍地安全就可以。
答案 1 :(得分:4)
我昨天碰到了与此非常类似的事情,但值得注意的是,我唯一一次遇到分段错误的时候是多个线程试图同时运行PyGILState_Ensure()
。
就我而言,这是因为我在初始化解释器时(在主线程中)没有调用PyEval_InitThreads()
。请注意,初始化后必须运行PyEval_ReleaseLock()
,否则下次调用PyGILState_Ensure()
时会发生死锁,因为PyEval_InitThreads()
会隐式获取GIL。
希望这有帮助。