【问题标题】:Why does PyGILState_Release(…) segfault in this case?为什么 PyGILState_Release(...) 在这种情况下会出现段错误?
【发布时间】:2011-07-05 16:31:38
【问题描述】:

我正在为PyAudio 实现异步音频播放。后端 Portaudio 通过创建自己的线程并在需要/有新音频数据时调用 C 回调函数来实现异步播放。每当调用该 C 回调函数时,我都会调用以前注册的 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 位变量,有时由PyGILState_Ensure() 设置为1,有时设置为0。只有在设置为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 线程,因此临时线程状态实际上正在被销毁,并且可能会发生很多事情。
  • 另外,您链接的是哪个版本? (GIL 代码在 3.2 中发生了相当大的变化,所以我看哪个版本会有所不同)
  • 我正在使用 Python 2.7.1 OSX

标签: python multithreading callback segmentation-fault portaudio


【解决方案1】:

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

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

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    我遇到了完全相同的问题。解决方法是在任何回调发生之前在主线程上调用 PyEval_InitThreads()

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-06
      • 1970-01-01
      • 2020-07-28
      • 2013-12-28
      • 2018-04-10
      • 1970-01-01
      相关资源
      最近更新 更多