C程序调用python程序

1 篇文章 0 订阅

最近跟同学一起做后台的项目,用的c语言。由于要用到python下的算法库,因此学习了在c程序中如何调用python程序。

由于后台的环境用到了多线程,因此需要考虑多线程环境下的c调用python。

在多线程环境下调用python库,需要按照如下步骤:

在主线程中:

1、初始化python,允许支持多线程。

2、python扩展初始化

3、释放线程全局锁

在子线程中:

4、设置GIL(python特有的全局解释锁)的状态,确保为当前线程控制GIL

5、调用ython代码

6、释放GIL,将GIL的控制权交还给系统

功能描述:我们需要在c代码中调用python函数,并且,要将c的参数传到python中,并解析python的返回值存入c的变量中。

示例:

#include<Python.h>

PyObject * pFunc = NULL;    //全局变量,可使process.c文件访问该变量

int pythonInit()    //自定义python初始化,包括python初始化以及相关模块参数的导入。
{
Py_Initialize();    //python初始化,库函数
if (!Py_IsInitialized())
{

    printf("Python init failed!\n");

            PyErr_Print();//打印错误信息,库函数

    return -1;
}

PyObject *pDict = NULL;           //python模块字典
PyObject *pModule = NULL;    //python模块


PyRun_SimpleString("import sys");    //调用python代码
PyRun_SimpleString("sys.path.append('./')");    //将当前目录添加到python路径


        //导入模块,模块名为query_python。在query_python.py文件中。

pModule = PyImport_Import(PyString_FromString("query_python"));    

if(!pModule)
{
printf("Load query_python.py failed!\n");
PyErr_Print();
return -1;
}
pDict = PyModule_GetDict(pModule);    //调用库函数获取模块字典,模块字典包含该模块下的所有函数
        if(!pDict)
{
printf("Can't find dict in query_python!\n");
                PyErr_Print();
return -1;
}

       pFunc = PyDict_GetItemString(pDict, "query_k");//获取模块字典中query_k函数

       if(!pFunc || !PyCallable_Check(pFunc))    //检查函数的有效性
       {
           printf("Can't find function!\n");
           PyErr_Print();
           return -1;
       }
       if(pModule)    //释放模块申请的内存
{
Py_DECREF(pModule);
}
if(pDict)     //释放模块字典申请的内存
{
Py_DECREF(pDict);
}
return 0;

}

//主调函数

void serverStart()

{

        PyEval_InitThreads();//初始化多线程,使python支持多线程

        int res;

res = pythonInit(); //python扩展初始化

        if(res==-1)
{
PyErr_Print();
return;

}

        //开启多线程之前,必须先释放锁,才能获得线程全局锁
         PyEval_ReleaseLock();

        ... //此处开启了多线程

        process();//process函数会调用process.c中的get_query_result函数

        PyGILState_Ensure();    //主线程结束,释放GIL
Py_Finalize();                //对应于python初始化的释放

}


process.c:

extern PyObject *pFunc;    //获取另一个文件中的全局变量

///下面的函数功能是获取query_python.py文件中的query_k函数的返回值,并传给c程序的query_res指向的内存。

int get_query_result(const char *buf, const char *query, int k, char *query_res)
{
    PyGILState_STATE gstate = PyGILState_Ensure();//确保当前线程获得GIL的控制权

    if (!PyCallable_Check(pFunc))
    {
        PyErr_Print();
        return -1;
    }

    PyObject *pArgs = NULL;     //传给PyObject_Call函数的第二个参数
    PyObject *pkwargs = NULL; //传给PyObject_Call函数的第三个参数

    pArgs = PyTuple_New(3);    //函数参数以元组形式传入,参数个数为3

    PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", query));    //传给python的参数,s代表字符串,i代表整数
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("s", buf));
    PyTuple_SetItem(pArgs, 2, Py_BuildValue("i", k));

    PyObject *pyValue = PyObject_Call(pFunc, pArgs, pkwargs);//传入pFunc指向的函数,参数。返回值存入pyValue中。

    if (pyValue == NULL)
    {
        printf("PyObject_CallObject error!\n");
        PyErr_Print();
        PyGILState_Release(gstate);    //返回值为NULL,需要释放GIL
        return -1;
    }
    char *ret_str;    //无需申请内存
    int len;

    PyArg_ParseTuple(pyValue, "si", &ret_str, &len);    //解析返回值,将返回值存入ret_str字符数组和len变量中。
    strncpy(query_res, ret_str, len);    //执行字符串拷贝。
    if(pArgs)
    {
        Py_DECREF(pArgs);    //释放参数内存
    }
    if(pkwargs)                    //释放参数内存
    {
        Py_XDECREF(pkwargs);
    }
    PyGILState_Release(gstate);    //释放GIL
    return 0;

}

总结:

第一次学习c调用python,查阅了大量资料,花了不少时间,收获还是不少。

调试过程中遇到了很多问题,暴露了c语言的一些不足,同时,尽管python学了一段时间了,但对pyhton的GIL理解不深。


  • 3
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值