由于Python解释器有全局解释所GIL的原因,导致在同一时刻只能有一个线程拥有解释器,所以在C++多线程调用python脚本时,需要控制GIL,线程获取GIL。
在主线程中初始化Python解释器环境,代码如下:
{
Py_Initialize(); //初始化Python环境
if ( !Py_IsInitialized() ) //检测是否初始化成功
{
return NULL;
}
else
{
PyEval_InitThreads(); //开启多线程支持
int nInit = PyEval_ThreadsInitialized(); //检测线程支持是否开启成功
if ( nInit )
{
PyEval_SaveThread(); //因为调用PyEval_InitThreads成功后,当前线程就拥有了GIL,释放当前线程的GIL,
}
}
}
然后可以创建子线程,在子线程调用中 加入如下代码:
int nHold = PyGILState_Check() ; //检测当前线程是否拥有GIL
PyGILState_STATE gstate;
if ( !nHold )
{
gstate = PyGILState_Ensure(); //如果没有GIL,则申请获取GIL
}
Py_BEGIN_ALLOW_THREADS;
Py_BLOCK_THREADS;
/**************************以下加入需要调用的python脚本代码 Begin***********************/
/**************************以下加入需要调用的python脚本代码 End***********************/
Py_UNBLOCK_THREADS;
Py_END_ALLOW_THREADS;
if (!nHold)
{
PyGILState_Release(gstate); //释放当前线程的GIL
}
boost::python对Python原生的C API有较好的封装,可以很方便通过C++对python脚本进行调用一下是一些学习总结,以便今后需要使用的时候能快速使用
假设有python模块 School.py 包含类 Student 和 函数 Call 和返回类对象的CallClass方法
class Student():
def __init__(name,age,properties):
self.name = name
self.age = age
self.properties = proterties
def show():
print(self.name, slef.age, self.properties)
def Call(p1,p2):
print(p1,p2)
return "yes"
def CallClass():
s = Student("universal",20,"good")
return s
以下为C++调用python脚本代码:
boost::python::handle<>* school_module = NULL;
string strModule = "School";
school_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str())); //通过模块名称获取模块handle
boost::python::object main_module(*manager_module); //转化为boost::python::object类型表示的模块
boost::python::object main_namespace = main_module.attr("__dict__"); //得到模块内的字典,字典中包含了 所有的类和函数
boost::python::handle<> Student; //定义一个Student类 的handle
//得到Student类的实例,参数可以任意传递 比如以下设置properties为一个dict
Student = boost::python::handle<>((PyRun_String(
"Student('alen',30,{\"errorcode\":0, \"message\":\"execute succeed\"})", Py_eval_input,
main_namespace.ptr(), main_namespace.ptr())));
/*********以下将properties设置为一个tuple
Student = boost::python::handle<>((PyRun_String(
"Student('alen',30,(1,2,3,4,5))", Py_eval_input,
main_namespace.ptr(), main_namespace.ptr())));
**********/
/*********以下将properties设置为一个list
Student = boost::python::handle<>((PyRun_String(
"Student('alen',30,[1,2,3,4,5])", Py_eval_input,
main_namespace.ptr(), main_namespace.ptr())));
**********/
/**********
//通过Student类调用类方法show
boost::python::object result = boost::python::call_method(Student.get(), "show");
2>调用脚本中的函数 Call 假设传递的第一个参数为string 第二个为int
boost::python::handle<>* school_module = NULL;
string strModule = "School";
school_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str())); //通过模块名称获取模块handle
boost::python::object result = boost::python::call_method(school_module->get(),"Call","schoolname",2);
3>调用脚本时,可以传递各种参数,boost 已经封装了boost::python::dict 对应dict boost::python::tuple对应 tuple
比如构造dict参数,代码如下:
boost::python::dict inParams;
inParams.setdefault("name","clear");
inParams.setdefault("code","56821334");
inParams.setdefault("number",123456);
boost::python::handle<>* school_module = NULL;
string strModule = "School";
school_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str())); //通过模块名称获取模块handle
boost::python::object student = boost::python::call_method(school_module->get(),"CallClass");
//可以通过返回的类对象 继续调用类Student 的 show方法 如下:
boost::python::object q = boost::python::call_method(student.ptr(), "show");
//也可以获取类的属性值 比如 name string 类型 和 age Int类型
boost::python::object obj_dict = student.attr("__dict__"); //获取对象的字典
//获取name
boost::python::object name = obj_dict["name"];
std::string sname = boost::python::extract(name);
//获取age
boost::python::object age = obj_dict["age"];
int age = boost::python::extract(age);
/*全局解释和线程锁*/
class PyGILThreadLock
{
public:
PyGILThreadLock()
{
_save = NULL;
nStatus = 0;
nStatus = PyGILState_Check() ; //检测当前线程是否拥有GIL
PyGILState_STATE gstate;
if ( !nStatus )
{
gstate = PyGILState_Ensure(); //如果没有GIL,则申请获取GIL
nStatus = 1;
}
_save = PyEval_SaveThread();
PyEval_RestoreThread(_save);
}
~PyGILThreadLock()
{
_save = PyEval_SaveThread();
PyEval_RestoreThread(_save);
if (nStatus)
{
PyGILState_Release(gstate); //释放当前线程的GIL
}
}
private:
PyGILState_STATE gstate;
PyThreadState *_save;
int nStatus;
};
{
PyGILThreadLock locker;
///以下添加python调用代码
}
使用boost::python的时候捕获异常获取异常信息
std::string GetPythonErrorInfo(void)
{
using namespace boost::python;
PyObject *exc,*val,*tb;
PyErr_Fetch(&exc,&val,&tb);
PyErr_NormalizeException(&exc,&val,&tb);
handle<> hexc(exc),hval(allow_null(val)),htb(allow_null(tb));
if(!hval)
{
return extract(str(hexc));
}
else
{
object traceback(import("traceback"));
object format_exception(traceback.attr("format_exception"));
object formatted_list(format_exception(hexc,hval,htb));
object formatted(str("").join(formatted_list));
return extract(formatted);
}
}
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
#define Check_Init(init) \
if(!init){\
cout<<"请先通过init指令初始化python环境"<* manager_module = NULL;
for ( int i = 0 ; i < 2000 ; i ++)
{
PyGILThreadLock locker;
manager_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str()));
sResult = boost::python::call_method(manager_module->get(),"threadcall",szMessage);
cout<* manager_module = NULL;
std::string sResult = "";
PyGILThreadLock locker;
try
{
manager_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str()));
sResult = boost::python::call_method(manager_module->get(),"say",szMessage);
}
catch(...)
{
delete manager_module;
manager_module = NULL;
cout<<"捕获一个异常在callsay方法中"<>szCommand;
while ( strcmp("quit",szCommand) != 0 )
{
if ( strcmp("threadcall",szCommand) == 0 )
{
Check_Init(bInit);
for ( int i = 0 ; i < 5; i++ )
{
_beginthreadex(NULL,
0,
ThreadCall,
NULL,
0,
NULL);
}
}
else if ( strcmp("init",szCommand) == 0 )
{
if ( bInit )
{
cout<<"Python 环境已经初始化"<>szCommand;
}
system("pause");
return 0;
}
添加一个测试过程中的代码, 因为还封装了类所以单独编译不过,用来标记一下吧 ,方便下次在使用的时候可以快速的重新学习
// PythonManager.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include
#include
#include
// #include
// #include "Python.h"
#include "PythonAssister.h"
using namespace std;
#define Check_Init(init) \
if(!init){\
cout<<"请先通过init指令初始化python环境"<* manager_module = NULL;
for ( int i = 0 ; i < 2000 ; i ++)
{
PyGILThreadLock locker;
// int nHold = PyGILState_Check() ; //检测当前线程是否拥有GIL
// PyGILState_STATE gstate;
// if ( !nHold )
// {
// gstate = PyGILState_Ensure(); //如果没有GIL,则申请获取GIL
// }
// Py_BEGIN_ALLOW_THREADS;
// Py_BLOCK_THREADS;
manager_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str()));
sResult = boost::python::call_method(manager_module->get(),"threadcall",szMessage);
cout<* manager_module = NULL;
manager_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str()));
boost::python::object result = boost::python::call_method(manager_module->get(),"calltest");
cout<< result.ptr()->ob_type->tp_name<(result.ptr(), "show");
boost::python::object r1 = result.attr("__dict__");
boost::python::object r2 = r1["name"];
// boost::python::call_method(r1,"__getdict__","name");
std::string sname = boost::python::extract(r2);
cout<* manager_module = NULL;
try
{
manager_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str()));
boost::python::object main_module(*manager_module);
boost::python::object main_namespace = main_module.attr("__dict__");
boost::python::handle<> Student;
Student = boost::python::handle<>((PyRun_String(
"Student('alen',30,{\"errorcode\":0, \"message\":\"execute succeed\"})", Py_eval_input,
main_namespace.ptr(), main_namespace.ptr())));
boost::python::object result = boost::python::call_method(Student.get(), "show");
}
catch(...)
{
PyErr_Print();
PyErr_Clear();
}
}
void calldict()
{
boost::python::handle<>* manager_module = NULL;
boost::python::dict mydict;
int nHold = PyGILState_Check() ; //检测当前线程是否拥有GIL
PyGILState_STATE gstate;
if ( !nHold )
{
gstate = PyGILState_Ensure(); //如果没有GIL,则申请获取GIL
}
Py_BEGIN_ALLOW_THREADS;
Py_BLOCK_THREADS;
try
{
boost::python::dict inParams;
inParams.setdefault("name","clear");
inParams.setdefault("code","56821334");
inParams.setdefault("number",123456);
int nLenDict = len(inParams);
const char* szType = inParams.ptr()->ob_type->tp_name;
boost::python::list dictlist;
dictlist = inParams.items();
int nLenList = len(dictlist);
for ( int i = 0 ; i< nLenList; i++ )
{
boost::python::object obj;
obj = dictlist.pop();
const char* szType = obj.ptr()->ob_type->tp_name;
int n = PyType_Check(obj.ptr());
int nLenTuple = len(obj);
for ( int j = 0 ; j (obj[j]);
boost::python::extractex((obj[j]));
if ( ex.check() )
{
cout<<"yes it is string"<(itemTuple[0]);
string strValue = boost::python::extract(itemTuple[1]);
itemTuple = dictlist.pop();
int a = 10;
}
manager_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str()));
//mydict = boost::python::call_method(manager_module->get(),"calldict",inParams);
boost::python::object result = boost::python::call_method(manager_module->get(),"calldict",inParams);
const char* szType1 = result.ptr()->ob_type->tp_name;
cout<<"Type : "<* manager_module = NULL;
std::string sResult = "";
int nHold = PyGILState_Check() ; //检测当前线程是否拥有GIL
PyGILState_STATE gstate;
if ( !nHold )
{
gstate = PyGILState_Ensure(); //如果没有GIL,则申请获取GIL
}
Py_BEGIN_ALLOW_THREADS;
Py_BLOCK_THREADS;
try
{
manager_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str()));
sResult = boost::python::call_method(manager_module->get(),"say",szMessage);
}
catch(...)
{
delete manager_module;
manager_module = NULL;
cout<<"捕获一个异常在callsay方法中"<>szCommand;
while ( strcmp("quit",szCommand) != 0 )
{
if ( strcmp("threadcall",szCommand) == 0 )
{
Check_Init(bInit);
for ( int i = 0 ; i < 5; i++ )
{
_beginthreadex(NULL,
0,
ThreadCall,
NULL,
0,
NULL);
}
}
else if ( strcmp("classtest",szCommand) == 0 )
{
classtest();
}
else if ( strcmp("init",szCommand) == 0 )
{
if ( bInit )
{
cout<<"Python 环境已经初始化"<>szCommand;
}
system("pause");
return 0;
}