bas*_*ibe 4 python multithreading callback portaudio segmentation-fault
我正在为PyAudio实现异步音频播放.后端Portaudio通过创建自己的线程并在需要/拥有新的音频数据时调用C回调函数来实现异步回放.每当调用C-callback函数时,我都会调用以前注册的Python函数,用户必须提供音频数据.
由于这种对Python的调用发生在非Python创建的线程中,因此文档说我必须PyGILState_Ensure()在调用Python之前调用它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;
}
Run Code Online (Sandbox Code Playgroud)
哪个段错误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 ()
Run Code Online (Sandbox Code Playgroud)
这对任何人都有意义吗?
我有完全相同的问题.修复是PyEval_InitThreads()在发生任何回调之前调用主线程.
我相信其原因如下.当Python解释器首次启动时,它避免初始化GIL,因为大多数Python程序都是单线程的,并且GIL的存在会导致一些小的性能损失.因此,如果没有初始化GIL PyGILState_Ensure()并PyGILState_Release()处理未初始化的数据,会导致整个地方发生奇怪的崩溃.
通过调用PyEval_InitThreads()GIL初始化PyGILState_Ensure()并PyGILState_Release()正常工作.如果GIL已经初始化PyEval_InitThreads()没有任何作用,那么一遍又一遍地安全调用是安全的.