【问题标题】:PyEval_CallObject failing in loop occasionallyPyEval_CallObject 偶尔会在循环中失败
【发布时间】:2010-02-10 05:07:11
【问题描述】:

我在使用 Python C API 时有些吃力。我正在调用 python 方法以大约 60hz 做一些游戏 AI。它在大多数的时间里工作,但每隔一秒左右对 PyEval_CallObject 的调用就会产生一个 NULL 返回值。如果我正确检测到错误并继续循环,则下一秒左右一切正常,然后错误再次发生。

我怀疑我在引用计数方面做错了,但我不知道它是什么:

int script_do_ai(struct game_data_t* gd)
{

    PyObject *pAiModule, *pResult;

    float result=0.0;
    pResult = NULL;

    pAiModule = PyImport_Import(PyString_FromString("ai_script"));

是的,我每次迭代都会导入模块。那有必要吗?如果我将 pAiModule 存储为全局,大约一秒钟后我会遇到严重崩溃。

    pResult = PyEval_CallObject(PyObject_GetAttrString(pAiModule, "do_ai"),
                               Py_BuildValue("f", gd->important_float))  
    if (pResult != NULL)
    {       
        PyArg_Parse(pResult, "f", &result);
        Py_DECREF(pResult);
        ConquerEnemies(result);  //you get the idea
    }
    else  //this happens every 75 or so iterations thru the loop
    {
       if (PyErr_ExceptionMatches(PyExc_SomeException))  //? not sure what to do here
       {

我还没有找到如何提取异常,要么...没有测试每个异常

       }
    }

我什至接近做到这一点吗?就像我说的那样,它大部分都有效,但我真的很想了解为什么我会出错。

提前感谢您的帮助。

【问题讨论】:

    标签: python c python-c-api


    【解决方案1】:

    您可以根据需要随时调用PyImport_Import(),但您只会继续获取相同的模块对象。 Python 缓存导入。此外,您应该只使用PyImport_ImportModule(),而不是创建一个新的 Python 字符串并泄漏引用(从而泄漏对象),它需要一个 const char *

    PyImport_Import*() 返回一个新的引用,不过,你应该在完成后调用Py_DECREF()。将模块存储在全局中应该不是问题,只要您拥有对它的引用(您在此处进行)。

    在您对PyEval_CallObject() 的调用中,您没有检查Py_BuildValue() 的结果是否有错误,并且您在完成后也没有调用Py_DECREF(),因此您将该对象泄漏为好吧。

    为了将 Python 浮点数转换为 C 双精度数,您可能应该只调用 PyFloat_AsDouble() 而不是使用 PyArg_Parse()(并记住要测试异常)

    深入到实际的错误处理:PyErr_ExceptionMatches() 仅在您真正想要测试异常是否匹配某些内容时才有用。如果你想知道是否发生了异常,或者获取实际的异常对象,PyErr_Occurred() 是你应该调用的。它返回当前异常type(不是实际的异常对象)作为借用引用,如果没有设置则返回NULL。如果您只想打印到 stderr 的回溯,PyErr_Print()PyErr_Clear() 是您想要使用的。为了更细粒度地检查代码中的实际错误,PyErr_Fetch() 为您提供当前异常对象和与之关联的回溯(它为您提供与 Python 代码中的 sys.exc_info() 相同的信息。)所有事情都很少考虑想深入了解 C 代码中的异常处理。

    【讨论】:

    • +1 谢谢...我想如果我内联对 Py_BuildValue 的调用,它会处理泄漏的引用,但可能不会。我会在 pyErr_Occurred 上查看 PyErr_Fetch 给我的信息
    • 唉,C 不是垃圾回收的 :) PyErr_Print() 非常方便打印异常,尤其是在调试这样的情况时。
    • 是的,我在 .NET 领域的时间太多了。好吧奇怪。我在我的 python 脚本中添加了“sys.stdout = open('CONOUT$', 'wt')”(输出到控制台......我在 Win32 中),一切都开始神奇地工作。奇怪。
    • 仅供参考...我修复了这个错误。原来我正在运行的循环没有同步到 60hz……它以 100% 的速度运行 cpu。我投入了 10 毫秒的非忙碌等待,一切似乎都很好。它至少解释了为什么在之前添加控制台输出“修复”了问题......这是一个时间问题。如果没有上述提示,就无法解决它……谢谢!
    猜你喜欢
    • 2021-08-20
    • 2018-08-08
    • 2018-04-09
    • 2020-08-08
    • 2015-03-26
    • 2013-03-03
    • 2014-10-14
    • 2011-06-10
    • 1970-01-01
    相关资源
    最近更新 更多