4

我正在为PyAudio实现异步音频播放。后端 Portaudio 通过创建自己的线程并在需要/有新音频数据时调用 C 回调函数来实现异步播放。每当调用 C 回调函数时,我都会调用先前注册的 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;
}

哪个段错误PyGILState_Release(gstate)。这个回调函数经常被调用。比如,每秒几百到几千次。这gstate是一个 32 位变量,有时设置为1,有时设置为0by PyGILState_Ensure()。它仅在设置为 时崩溃1。通常,会有 11后跟 2 到 4 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 ()

这对任何人都有意义吗?

4

2 回答 2

6

我有完全相同的问题。修复是PyEval_InitThreads()在任何回调发生之前调用主线程。

我认为原因如下。当 Python 解释器第一次启动时,它会避免初始化 GIL,因为大多数 Python 程序都是单线程的,并且 GIL 的存在会导致一些小的性能损失。因此,如果没有初始化 GILPyGILState_Ensure()PyGILState_Release()处理未初始化的数据,就会导致到处都是奇怪的崩溃。

通过调用PyEval_InitThreads()GIL 被初始化并PyGILState_Ensure()正常PyGILState_Release()工作。如果 GIL 已经初始化PyEval_InitThreads(),什么都不做,所以一遍又一遍地调用是安全的。

于 2013-02-16T23:49:40.580 回答
4

昨天我遇到了与此非常相似的事情,尽管值得注意的是,我唯一一次遇到分段错误是多个线程尝试同时运行PyGILState_Ensure()时。

PyEval_InitThreads()就我而言,这是由于我在初始化解释器(在主线程中)时没有调用造成的。请注意,必须PyEval_ReleaseLock()在初始化后运行,否则下次调用时会死锁PyGILState_Ensure(),因为PyEval_InitThreads()隐式获取 GIL。

希望这可以帮助。

于 2011-05-02T01:54:28.183 回答