使用boost.python时进程异常失败

1 投票
1 回答
644 浏览
提问于 2025-04-18 07:42

我使用boost.python来调用脚本。每个Python脚本都有三个功能:

  • 初始化 - 这个功能必须在开始时调用
  • 反初始化 - 这个功能必须在结束时调用,用来关闭所有对象。
  • 处理 - 这个功能必须在初始化和反初始化之间多次调用。

这个类不需要解释:

class PyInit
{
public:
    PyInit() { Py_Initialize(); }
    ~PyInit() {  Py_Finalize();  }
};

这个类创建了一个PyInit对象实例,然后初始化所有Python对象,并在构造函数中调用"Initialize()"这个Python函数。并在析构函数中调用"Uninitialize()"。

这个类还有一个"Process"函数,可以在类外部多次调用。

class PyGuard
{
public:
    explicit PyGuard(const char* pypath)
    {
      try
      {
        _main_module = boost::python::import("__main__");
        _main_namespace = _main_module.attr("__dict__");
        boost::python::object ignored = exec_file(pypath, _main_namespace);
        _Initialize = _main_namespace["Initialize"];
        _Process = _main_namespace["Process"];
        _Uninitialize = _main_namespace["Uninitialize"];

        _InitRes = _Initialize();
        _ProcRes = boost::python::object();
      }
      catch(boost::python::error_already_set&)
      {
        string res;
        py_utils::err_parse(res);
        throw string("Python exception: " + res);
      }
      catch(std::exception& e)
      {
          throw string("C++ exception: " + string(e.what()));
      }
      catch(...)
      {
          throw string("Unhandled exception!");
      }
    }
    virtual ~PyGuard()
    {
        _Uninitialize(_InitRes, _ProcRes);
    }
    void Process()
    {
        _ProcRes = _Process(_InitRes, _ProcRes);
    }


private:
    py_utils::PyInit _initPython;

    boost::python::object _InitRes;
    boost::python::object _ProcRes; 

    boost::python::object _Process;
    boost::python::object _Initialize;
    boost::python::object _Uninitialize;

    boost::python::object _main_module;
    boost::python::object _main_namespace;
};

只有"Process"函数会出现"boost::python::error_already_set"的异常,这时当前的PyGuard实例会被删除,然后重新创建一个新的。这样做是为了避免可能隐藏的依赖关系,这些依赖关系可能是隐藏异常的结果。

所以,在出现异常后,所有的Python相关内容都会被移除(甚至会调用Py_Finalize()),然后再重新创建。

不过,经过4到10次这样的异常后,整个C++进程会崩溃。

崩溃时,连捕获异常的代码都无法处理:

  try
  {
      _PyGuard = make_shared<PyGuard>("my script");
      while(true)
      {
        try {
          _PyGuard->Process();
        }
        catch()
        {
              bool inited = false;
              while(!inited)
              {
                try
                {
                _pyGuard = nullptr;
                _pyGuard = make_shared<PyGuard>("script path.txt");
                inited = true;
                }
                catch(string& e)
                {
                }
              }
        }
      } 
      _PyGuard = nullptr;
  } 
  catch(...)
  {
      //falling, it never being catched here.
  }

所以,我的问题是,为什么会崩溃并且无法捕获?

我发现崩溃发生在这一行:

_pyGuard = make_shared<PyGuard>("script path.txt"); 

调用时,所以我觉得这是Python抛出的异常,无法被捕获。为什么会这样?如何防止它?

1 个回答

1

你可能在使用 Py_Finalize() 时遇到了问题。根据 Boost 1.55 的文档,你不应该在使用 Boost.Python 时调用 Py_Finalize()。看起来你的程序其实并不需要这个结束操作,所以你可以试着把 ~PyInit() 中的这个调用去掉。

如果你真的有某种原因需要进行结束操作,可以看看 Py_NewInterpreter()

至于你提到的“无法捕获的异常”问题,这通常是因为同时有两个活动的异常。当这种情况发生时,C++ 会直接中止程序。这可能就是你代码中发生的情况(也可能不是)。

撰写回答