问题描述

c++Qt调用Python脚本自定义函数,在py_initialize时,崩溃退出。或者在线程中的某个接口处崩溃。感觉多线程对Python脚本是极不安全,很容易造成线程问题。因为我在main中主线程调用却没有任何问题……

Patal Python error: init_importlib: can’t impart _frozen_importlib
报错好像是MATLAB的库崩溃咯……
在这里插入图片描述
以下是示例代码,来自AI

#include <Python.h>
#include <iostream>
#include <string>

int main() {
   // 初始化Python解释器
   Py_Initialize();

   // 添加包含自定义Python模块的目录到sys.path
   std::string module_path = "/path/to/your/module"; // 替换为你的模块目录
   std::string command = "import sys\nsys.path.append('" + module_path + "')";
   PyRun_SimpleString(command.c_str());

   // 导入Python模块
   PyObject* pModule = PyImport_ImportModule("your_module_name"); // 替换为你的模块名
   if (!pModule) {
       PyErr_Print();
       std::cerr << "Failed to load module." << std::endl;
       return -1;
   }

   // 获取Python函数
   PyObject* pFunc = PyObject_GetAttrString(pModule, "your_function_name"); // 替换为你的函数名
   if (!pFunc || !PyCallable_Check(pFunc)) {
       PyErr_Print();
       std::cerr << "Cannot find function." << std::endl;
       return -1;
   }

   // 准备调用Python函数的参数
   PyObject* pArgs = PyTuple_New(2); // 假设函数需要两个参数
   PyTuple_SetItem(pArgs, 0, PyLong_FromLong(1)); // 第一个参数
   PyTuple_SetItem(pArgs, 1, PyLong_FromLong(2)); // 第二个参数

   // 调用Python函数
   PyObject* pValue = PyObject_CallObject(pFunc, pArgs);
   if (!pValue) {
       PyErr_Print();
       std::cerr << "Function call failed." << std::endl;
       return -1;
   }

   // 处理返回值
   long result = PyLong_AsLong(pValue);
   std::cout << "Function returned: " << result << std::endl;

   // 清理
   Py_DECREF(pModule);
   Py_DECREF(pFunc);
   Py_DECREF(pArgs);
   Py_DECREF(pValue);

   // 关闭Python解释器
   Py_Finalize();

   return 0;
}

解决方案

经过几天的测试,可能有几处需要注意的地方
1.pyObiect那些不一定要使用Py_DECREF来释放,没调用挺好,多线程一调用就有可能崩溃,调用的顺序也非常有讲究,最好就是用完就调用释放,多线程时,我就都没有调用释放函数;
2.py_initialize这个函数很关键,必须在线程中启用,之后可以写上面的那些pyobject等等语句。还有一种现象就是,多线程时,很容易崩在前面准备的位置;所以这里的解决方法就是将py_initialize函数放在一个函数中,并加锁。意思是一个个线程依次调用。
3.py_finitialize函数很奇怪。理论上跟initialize函数成对出现才对,但是多线程就是不能调,调用了该函数,程序就会出错崩溃。所以多线程也不调用该函数,至于为什么,真的不清楚…
4.PyGILState_Ensure函数也很诡异,跟PyGILState_Release成对出现,理论上在同一个函数中,怎么都会执行,最后跑着跑着,所有线程就被卡死死锁了,估计根本就没释放啊!在多线程中也没有用。

#include <Python.h>
#include <iostream>
#include <thread>

void call_python_function() {
   try {
       // 尝试获取GIL
       PyGILState_STATE gstate = PyGILState_Ensure();
       std::cout << "GIL acquired successfully." << std::endl;

       // 执行Python代码...

       // 释放GIL
       PyGILState_Release(gstate);
       std::cout << "GIL released successfully." << std::endl;
   } catch (const std::exception& e) {
       std::cerr << "Exception occurred: " << e.what() << std::endl;
   } catch (...) {
       std::cerr << "Unknown exception occurred." << std::endl;
   }
}

int main() {
   // 初始化Python
   Py_Initialize();

   // 创建并启动线程
   std::thread t(call_python_function);
   t.join();

   // 清理Python
   Py_Finalize();

   return 0;
}

最后总结,就是多线程在py_initialize函数前面加锁,不用Py_DECREF,py_finitialize,PyGILState_Ensure,PyGILState_Release。

在这里插入图片描述

这是我这几天测试的结果,至少目前程序运行还是正常的,会不会有其他问题,以后再更吧。

编辑:myhappyandy

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐