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

第二次调用PyObject_CallObject触发Segmentation Fault问题求助

问题根源与修复方案

我帮你排查出几个关键问题,正是这些导致了第二次调用时的段错误:

1. 值传递导致Python解释器提前终止

你的task函数参数是PyRecognitionContext pr——这是值传递,会触发默认拷贝构造函数,把原对象的所有指针(module、func等)直接复制给局部的pr对象。当第一个task执行完毕时,局部的pr会调用析构函数~PyRecognitionContext(),里面的Py_Finalize()会直接关闭Python解释器!这时候原对象里的所有PyObject指针都变成了无效的野指针,第二次调用RunModel时,操作的是已经被销毁的Python环境,必然触发段错误。

修复方式:把task的参数改成引用传递,同时禁用对象的拷贝构造和赋值运算符,从根源避免意外拷贝:

// 修改task函数参数为引用
int task(PyRecognitionContext& pr, string fileName) {
    // ... 原有代码不变
}

// 在PyRecognitionContext类中添加禁用拷贝的声明
PyRecognitionContext(const PyRecognitionContext&) = delete;
PyRecognitionContext& operator=(const PyRecognitionContext&) = delete;

2. Python对象引用计数管理混乱

你的代码多处没有正确管理Python对象的引用计数,不仅会导致内存泄漏,还可能间接引发对象被提前回收:

  • LoadModel中,第一次获取的load函数对象func,在重新赋值为run函数时,没有释放原对象;
  • args在LoadModel和RunModel中被重复赋值,旧的args对象没有被释放;
  • PyString_FromString、PyByteArray_FromStringAndSize创建的临时对象,没有调用Py_DECREF释放;
  • RunModel中返回的PyString_AsString(result)是Python内部的字符串指针,一旦result被Python GC回收,这个指针就会变成野指针。

修复方式:

  • 每次创建PyObject后,在不需要时调用Py_DECREF释放;
  • 返回字符串时,拷贝一份到C++的堆内存中再返回,避免依赖Python内部内存。

3. 其他潜在问题

  • 多线程场景下(你注释掉的代码),需要手动管理Python的GIL(全局解释器锁),否则会引发线程安全问题;
  • 缺少错误检查,比如PyObject_GetAttrString、PyObject_CallObject可能返回NULL,需要捕获并处理这些错误。

修改后的完整代码

#include <Python.h>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <thread>
#include <cstring>
using namespace std;

class PyRecognitionContext {
    PyObject *module, *func, *result, *args;
public:
    PyRecognitionContext(int argc, char **argv) 
        : module(nullptr), func(nullptr), result(nullptr), args(nullptr) {
        Py_Initialize();
        PySys_SetArgv(argc, argv);
        PyObject* moduleName = PyString_FromString("model.t");
        module = PyImport_Import(moduleName);
        Py_DECREF(moduleName); // 释放临时创建的模块名对象
        if (module == NULL) {
            PyErr_Print();
            throw std::invalid_argument("fails to import the module");
        }
    }

    ~PyRecognitionContext() {
        // 按顺序释放所有持有的Python对象
        if (func) Py_DECREF(func);
        if (args) Py_DECREF(args);
        if (result) Py_DECREF(result);
        if (module) Py_DECREF(module);
        Py_Finalize();
    }

    // 禁用拷贝构造和赋值运算符,防止对象拷贝
    PyRecognitionContext(const PyRecognitionContext&) = delete;
    PyRecognitionContext& operator=(const PyRecognitionContext&) = delete;

    void LoadModel(std::string model) {
        // 先释放之前的func(如果存在)
        if (func) Py_DECREF(func);
        func = PyObject_GetAttrString(module, "load");
        if (!func) {
            PyErr_Print();
            throw runtime_error("Failed to get 'load' function");
        }

        // 准备调用load函数的参数
        if (args) Py_DECREF(args);
        args = PyTuple_New(1);
        PyObject* modelStr = PyString_FromString(model.c_str());
        PyTuple_SetItem(args, 0, modelStr); // PyTuple_SetItem会接管对象引用,无需手动DECREF
        PyObject* loadResult = PyObject_CallObject(func, args);
        if (!loadResult) {
            PyErr_Print();
            throw runtime_error("Failed to call 'load' function");
        }
        Py_DECREF(loadResult); // 释放load函数的返回值

        // 切换到run函数
        Py_DECREF(func);
        func = PyObject_GetAttrString(module, "run");
        if (!func) {
            PyErr_Print();
            throw runtime_error("Failed to get 'run' function");
        }
    }

    char* RunModel(const char *imageBytes, size_t size) {
        if (!func) {
            throw runtime_error("Model not loaded, call LoadModel first");
        }

        // 准备调用run函数的参数
        if (args) Py_DECREF(args);
        args = PyTuple_New(1);
        PyObject* byteArray = PyByteArray_FromStringAndSize(imageBytes, size);
        PyTuple_SetItem(args, 0, byteArray); // 接管对象引用

        result = PyObject_CallObject(func, args);
        if (!result) {
            PyErr_Print();
            throw runtime_error("Failed to call 'run' function");
        }

        // 拷贝Python字符串到C++堆内存,避免野指针
        const char* pyStr = PyString_AsString(result);
        if (!pyStr) {
            Py_DECREF(result);
            throw runtime_error("Result is not a valid string");
        }
        char* resultStr = new char[strlen(pyStr) + 1];
        strcpy(resultStr, pyStr);

        Py_DECREF(result); // 释放Python的result对象
        return resultStr;
    }
};

int task(PyRecognitionContext& pr, string fileName) {
    cout << "//";
    cout << fileName << endl;
    std::ifstream ifs(fileName, ios::binary|ios::ate);
    if (!ifs.is_open()) {
        cerr << "Failed to open file: " << fileName << endl;
        return -1;
    }
    size_t imgSize = ifs.tellg();
    char *imageData = new char[imgSize];
    ifs.seekg(0, ios::beg);
    ifs.read(imageData, imgSize);
    ifs.close();

    char* result = pr.RunModel(imageData, imgSize);
    std::cout << result << endl;
    delete[] imageData;
    delete[] result; // 释放RunModel返回的堆内存
    return 0;
}

int main(int argc, char *argv[]) {
    try {
        PyRecognitionContext pr(argc, argv);
        pr.LoadModel("/home/dickzhou/data/sex_256/cp/");
        cout << "//loaded" << endl;
        task(pr, "/home/dickzhou/data/face/male/24-28/24/135.JPG");
        task(pr, "/home/dickzhou/data/face/female/24-28/24/138.JPG");
        // 多线程场景需要手动管理GIL
        // thread t1([&pr](){ 
        //     PyGILState_STATE gstate = PyGILState_Ensure(); 
        //     task(pr, "/home/dickzhou/data/face/male/24-28/24/135.JPG"); 
        //     PyGILState_Release(gstate); 
        // });
        // thread t2([&pr](){ 
        //     PyGILState_STATE gstate = PyGILState_Ensure(); 
        //     task(pr, "/home/dickzhou/data/face/female/24-28/24/125.JPG"); 
        //     PyGILState_Release(gstate); 
        // });
        // t1.join();
        // t2.join();
        cout << "//done" << endl;
    } catch (const exception& e) {
        cerr << "Error: " << e.what() << endl;
        return 1;
    }
    return 0;
}

简化后的编译命令

g++ -I/usr/include/python2.7 -std=c++11 -g -Wall -Wextra -lpthread -lpython2.7 pdr.cpp -o pdr

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:43:30