为什么PyGILState_Release(...)在这种情况下会发生错误?

时间:2011-02-28 10:49:34

标签: python multithreading callback segmentation-fault portaudio

我正致力于为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 ()

这对任何人都有意义吗?

2 个答案:

答案 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。

希望这有帮助。