C++ 多线程调用Python脚本

由于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脚本代码:
1>  在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);





4>处理返回的数据比如 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 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);


将全局解释器锁和线程的相关操作用类封装,使用构造函数和析构函数 去做locker 和 unlocker的操作

/*全局解释和线程锁*/
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);                       
	}                                                        
}   




以下为VC控制台测试程序

#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;
}









你可能感兴趣的:(C++,Python)