C语言多线程Python扩展中的段错误

4 投票
1 回答
1401 浏览
提问于 2025-04-15 20:36

我有一个简化的例子,它会导致程序崩溃,但我就是找不到解决办法。这个Python脚本调用了一个C语言的函数,这个函数又用pthread创建了一个新线程。我在新线程中使用了PyGILState_Ensure和PyGILState_Release来处理Python调用(PyRun_SimpleString),但可能我用得不对,或者漏掉了其他步骤。当我把receive_audio函数中的Python调用注释掉后,程序崩溃的问题就不再出现了。有没有什么想法?

输出结果:

python lib/test.py
(主线程)模块初始化完成
(主线程)调用run_thread()
(主线程)创建线程
(新线程)在receive_audio()中 - 获取GIL
(新线程)打印Python信息!
(新线程)在receive_audio()中 - 释放GIL
(新线程)循环0
程序崩溃

C语言代码如下:

PyMODINIT_FUNC streamaudio() {
    PyObject *m = Py_InitModule("streamaudio", methods);
    PyEval_InitThreads();
    mainThreadState = PyThreadState_Get();
    PyEval_ReleaseLock();
    printf("(Main Thread) initmodule complete\n");
}

static PyObject* run_thread(PyObject* self, PyObject* args)
{
    int ok, stream_id;
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();
    ok = PyArg_ParseTuple(args, "i", &stream_id);
    PyRun_SimpleString("print '(Main Thread) Creating thread'\n");
    int rc = pthread_create(&thread, NULL, receive_audio, (void*)stream_id);
    PyRun_SimpleString("print '(Main Thread) Thread created'\n");
    PyGILState_Release(gstate);
    return Py_BuildValue("i", rc);
}

void* receive_audio(void *x)
{
    printf("(New Thread) In receive_audio() - acquiring GIL\n");
    PyGILState_STATE gstate;
    gstate = PyGILState_Ensure();
    PyRun_SimpleString("print '(New Thread) python print!'\n");
    PyGILState_Release(gstate);
    printf("(New Thread) In receive_audio() - released GIL\n");
    int i;
    for (i = 0; i < 100; i++) {
    printf("(New Thread) Looping %d\n", i);
    sleep(1);
    }
}

1 个回答

3

我不确定这是否跟你的问题有关,但有一件事看起来有点可疑,就是你在模块初始化函数中调用了 PyEval_ReleaseLock()。我怀疑 Python 不会希望你的模块初始化器在它运行时释放全局解释器锁(GIL),而且我快速查看了一些示例代码 这里 也没有看到类似的做法。你能试着去掉那个 PyEval_ReleaseLock() 的调用,然后告诉我们发生了什么吗?

顺便说一下,我同意在 run_thread() 中的 PyGILState_*() 调用应该没有影响;你可以直接把它们去掉。

撰写回答