Python 3.12 C-API结合OpenMP调用PyUnicode_FromString出现段错误
问题原因与解决方法
核心原因
Python 3.12对内存分配子系统(obmalloc)做了硬性变更:所有涉及Python对象内存分配的操作,要求当前线程必须关联有效的Python解释器状态与GIL(全局解释器锁)。
你的代码中,OpenMP创建的线程(哪怕通过#pragma omp single限定单线程执行)并未被Python线程机制初始化:
- 单字符字符串
"a"触发了Python的小对象缓存机制,不需要走完整的_PyObject_Malloc流程,因此未暴露问题; - 多字符字符串
"aa"需要分配新内存块,触发_PyObject_Malloc时,线程因无有效解释器状态直接触发段错误(对应回溯中_PyInterpreterState_GET调用失败)。
解决方法
在OpenMP线程中调用Python API前,必须手动初始化线程状态并获取GIL,操作完成后释放GIL。使用PyGILState_Ensure()和PyGILState_Release()即可完成这一流程。
修改后的代码
#include <iostream> #include "omp.h" #include "Python.h" int main() { Py_Initialize(); #pragma omp parallel { #pragma omp single { // 初始化线程状态并获取GIL PyGILState_STATE gstate = PyGILState_Ensure(); std::cout << "One character:"<<std::endl; PyObject *nameobj1 = PyUnicode_FromString("a"); std::cout << nameobj1 << std::endl; Py_DECREF(nameobj1); std::cout << "Two characters:"<<std::endl; PyObject *nameobj2 = PyUnicode_FromString("aa"); std::cout << nameobj2 << std::endl; Py_DECREF(nameobj2); // 释放GIL并清理线程状态 PyGILState_Release(gstate); } } Py_Finalize(); }
验证说明
- 编译命令保持不变,Python 3.12环境下运行将不再触发段错误;
PyGILState_Ensure()会自动为非Python创建的线程初始化必要的解释器状态,并获取GIL;PyGILState_Release()会释放GIL并清理线程状态,避免资源泄漏。
补充说明
- 即使OpenMP仅在单个线程执行Python操作,该线程由OpenMP runtime创建,不属于Python线程池管理范畴,因此必须手动初始化状态;
- Python 3.11及更早版本中,内存分配的状态检查未做严格限制,因此未触发该问题。
内容的提问来源于stack exchange,提问作者fffred
相关产品推荐
相关产品推荐

