You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python/C API调用含scipy.optimize.least_squares的Python函数时挂起

问题:C++通过Python/C API调用含scipy导入的Python函数时挂起

问题描述

  • 在C++中使用Python/C API调用Python函数,只要被调用的Python代码执行from scipy.optimize import least_squares就会挂起,哪怕并未调用least_squares函数本身。
  • 单独编写的测试程序可正常运行,但将代码集成到大型程序中后出现挂起问题。
  • GDB调试发现:测试程序会启动十几个C/Python API相关线程,而大型程序中未启动这些线程;中断后显示挂起在获取GIL(全局解释器锁)的环节,调用栈如下。

C++调用代码

PyObject* pTestModuleName = PyUnicode_FromString( "test_clib" );
PyObject* pTestModule     = PyImport_Import( pTestModuleName );
PyObject* pFunction       = PyObject_GetAttrString( pTestModule, "testfunc1" );

const char* str = "foo";

PyObject* pArgList = Py_BuildValue( "(s)", str );

PyObject* pKeywords = PyDict_New();
PyDict_SetItemString( pKeywords, "bar", Py_True );

PyObject* pReturn = PyObject_Call( pFunction, pArgList, pKeywords );

GDB调试调用栈

^C
Thread 18 "acamd" received signal SIGINT, Interrupt.0x00007ffff73ca7e8 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
(gdb) where
#0  0x00007ffff73ca7e8 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib6/libpthread.so.0
#1  0x00007ffff793bef5 in take_gil () from /lib64/libpython3.9.so.1.0    
#2  0x00007ffff793c112 in PyEval_RestoreThread () from /lib64/libpython3.9.so.1.0
#3  0x00007ffff7a5dd8c in PyGILState_Ensure () from /lib64/libpython3.9.so.1.0
#4  0x00007fffb83bd6c5 in pybind11::detail::get_internals() () from /usr/local/lib64/python3.9/site-packages/scipy/spatial/_distance_pybind.cpython-39-x86_64-linux-gnu.so
#5  0x00007fffb83ae22c in PyInit__distance_pybind () from /usr/local/lib64/python3.9/site-packages/scipy/spatial/_distance_pybind.cpython-39-x86_64-linux-gnu.so
#6  0x00007ffff7a765bc in _imp_create_dynamic () from /lib64/libpython3.9.so.1.0
#7  0x00007ffff7989ac8 in cfunction_vectorcall_FASTCALL () from /lib6/libpython3.9.so.1.0
#8  0x00007ffff799a8eb in PyObject_Call () from /lib64/libpython3.9.so.1.0
#9  0x00007ffff7a089d8 in _PyEval_EvalFrameDefault () from /lib64/libpython3.9.so.1.0
#10 0x00007ffff79f084e in _PyEval_EvalCode () from /lib64/libpython3.9.so.1.0
#11 0x00007ffff79f1e2b in _PyFunction_Vectorcall () from /lib64/libpython3.9.so.1.0
    :   :   :

解决方案

1. 严格规范GIL管理

大型多线程程序中,Python/C API的GIL操作必须遵循规则:

  • 调用任何Python API前,通过PyGILState_Ensure()获取GIL;调用完成后,用PyGILState_Release()释放。
  • 排查大型程序中是否存在其他线程持有GIL未释放的情况,导致当前线程无法获取锁。

2. 初始化Python时启用线程支持

确保在初始化Python环境时,开启线程支持:

Py_SetProgramName(argv[0]); // 建议设置,确保Python路径正确
Py_Initialize();
PyEval_InitThreads(); // 初始化Python线程管理结构
PyEval_SaveThread(); // 释放主线程GIL,允许其他线程获取

如果未调用PyEval_InitThreads(),Python不会创建线程相关基础结构,scipy中依赖pybind11的扩展模块会因无法正常获取GIL而挂起。

3. 检查大型程序线程模型

scipy部分模块(如pybind11实现的扩展)依赖Python线程支持。若大型程序限制了线程创建(如禁用pthread),会导致Python无法启动必要线程,进而引发GIL死锁。

4. 延迟导入scipy模块

在Python函数内部延迟导入least_squares,避免模块初始化阶段的线程冲突:

def testfunc1(foo, bar=True):
    from scipy.optimize import least_squares
    # 后续业务代码

内容的提问来源于stack exchange,提问作者astronomerdave

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 17:22:47