DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  4737 随笔 :: 2 文章 :: 542 评论 :: 1615万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

由于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<boost::python::object>(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<boost::python::object>(school_module->get(),"Call","schoolname",2);
 
 

3>调用脚本时,可以传递各种参数,boost 已经封装了boost::python::dict 对应dict       boost::python::tuple对应 tuple

比如构造dict参数,代码如下:

 

 
 
boost::python::dict inParams;
 
inParams.setdefault<std::string,std::string>("name","clear");
 
inParams.setdefault<std::string,std::string>("code","56821334");
 
inParams.setdefault<std::string,int>("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<boost::python::object>(school_module->get(),"CallClass");
 
//可以通过返回的类对象 继续调用类Student 的 show方法 如下:
 
boost::python::object q = boost::python::call_method<boost::python::object>(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<std::string>(name);
 
//获取age
 
boost::python::object age = obj_dict["age"];
 
int age = boost::python::extract<int>(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<std::string>(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<std::string>(formatted);
 
}
 
}
 

 

以下为VC控制台测试程序
 
 
#include "stdafx.h"
 
#include <Windows.h>
 
#include <iostream>
 
#include <process.h>
 
#include <boost/python.hpp>
 
using namespace std;
 
 
 
#define Check_Init(init) \
 
if(!init){\
 
cout<<"请先通过init指令初始化python环境"<<endl;\
 
goto __next;\
 
}
 
 
 
 
 
bool bInit = false;
 
std::string strModule = "manager";
 
 
 
unsigned int __stdcall ThreadCall(void* lparam)
 
{
 
char szMessage[1024] = { 0 };
 
int id = GetCurrentThreadId();
 
sprintf_s(szMessage,sizeof(szMessage),"thread id: %d ",id);
 
std::string sResult = "";
 
boost::python::handle<>* 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<std::string>(manager_module->get(),"threadcall",szMessage);
 
cout<<sResult.c_str()<<endl;
 
 
 
delete manager_module;
 
manager_module = NULL;
 
}
 
return 0;
 
}
 
 
 
void callsay(const char* szMessage)
 
{
 
boost::python::handle<>* 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<std::string>(manager_module->get(),"say",szMessage);
 
}
 
catch(...)
 
{
 
delete manager_module;
 
manager_module = NULL;
 
cout<<"捕获一个异常在callsay方法中"<<endl;
 
}
 
delete manager_module;
 
manager_module = NULL;
 
}
 
 
 
int _tmain(int argc, _TCHAR* argv[])
 
{
 
char szCommand[20]={0} ;
 
cout<<"请输入指令"<<endl;
 
cin>>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 环境已经初始化"<<endl;
 
continue;
 
}
 
Py_Initialize(); //初始化Python环境
 
if ( !Py_IsInitialized() ) //检测是否初始化成功
 
{
 
cout<<"Python环境初始化失败"<<endl;
 
return NULL;
 
}
 
else
 
{
 
PyEval_InitThreads(); //开启多线程支持
 
int nInit = PyEval_ThreadsInitialized(); //检测线程支持是否开启成功
 
if ( nInit )
 
{
 
PyEval_SaveThread(); //因为调用PyEval_InitThreads成功后,当前线程就拥有了GIL,释放当前线程的GIL,
 
}
 
bInit = true;
 
cout<<"Python环境初始化成功,退出前请使用free指令释放Python环境"<<endl;
 
}
 
}
 
else if ( strcmp("say",szCommand) == 0 )
 
{
 
Check_Init(bInit);
 
std::string msg = "Python Manager";
 
callsay(msg.c_str());
 
}
 
else if ( strcmp("free",szCommand) == 0 )
 
{
 
Py_Finalize();
 
bInit = false;
 
cout<<"Python解释器环境被释放"<<endl;
 
}
 
else{
 
cout<<"未识别的指令信息"<<endl;
 
 
 
}
 
__next:
 
memset(szCommand,0,20);
 
cin>>szCommand;
 
}
 
system("pause");
 
return 0;
 
}
 

添加一个测试过程中的代码, 因为还封装了类所以单独编译不过,用来标记一下吧 ,方便下次在使用的时候可以快速的重新学习

 

 
 
// PythonManager.cpp : Defines the entry point for the console application.
 
//
 
 
 
#include "stdafx.h"
 
#include <Windows.h>
 
#include <iostream>
 
#include <process.h>
 
// #include <boost/python.hpp>
 
// #include "Python.h"
 
#include "PythonAssister.h"
 
using namespace std;
 
 
 
#define Check_Init(init) \
 
if(!init){\
 
cout<<"请先通过init指令初始化python环境"<<endl;\
 
goto __next;\
 
}
 
 
 
 
 
bool bInit = false;
 
std::string strModule = "manager";
 
 
 
unsigned int __stdcall ThreadCall(void* lparam)
 
{
 
char szMessage[1024] = { 0 };
 
int id = GetCurrentThreadId();
 
sprintf_s(szMessage,sizeof(szMessage),"thread id: %d ",id);
 
std::string sResult = "";
 
boost::python::handle<>* 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<std::string>(manager_module->get(),"threadcall",szMessage);
 
cout<<sResult.c_str()<<endl;
 
// Py_UNBLOCK_THREADS;
 
// Py_END_ALLOW_THREADS;
 
// if (!nHold)
 
// {
 
// PyGILState_Release(gstate); //释放当前线程的GIL
 
// }
 
}
 
return 0;
 
}
 
 
 
 
 
void calltest()
 
{
 
PyGILThreadLock locker;
 
boost::python::handle<>* manager_module = NULL;
 
manager_module = new boost::python::handle<>(PyImport_ImportModule((char*)strModule.c_str()));
 
boost::python::object result = boost::python::call_method<boost::python::object>(manager_module->get(),"calltest");
 
cout<< result.ptr()->ob_type->tp_name<<endl;
 
boost::python::object q = boost::python::call_method<boost::python::object>(result.ptr(), "show");
 
boost::python::object r1 = result.attr("__dict__");
 
boost::python::object r2 = r1["name"];
 
// boost::python::call_method<boost::python::object>(r1,"__getdict__","name");
 
std::string sname = boost::python::extract<std::string>(r2);
 
cout<<sname.c_str()<<endl;
 
delete manager_module;
 
manager_module = NULL;
 
 
 
}
 
 
 
void classtest()
 
{
 
PyGILThreadLock locker;
 
boost::python::handle<>* 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<boost::python::object>(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<std::string,std::string>("name","clear");
 
inParams.setdefault<std::string,std::string>("code","56821334");
 
inParams.setdefault<std::string,int>("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 <nLenTuple; j++ )
 
{
 
int n1 = PyType_Check(((boost::python::object)obj[j]).ptr());
 
std::string value = boost::python::extract<std::string>(obj[j]);
 
 
 
boost::python::extract<const std::string&>ex((obj[j]));
 
if ( ex.check() )
 
{
 
cout<<"yes it is string"<<endl;
 
}
 
cout<<value.c_str()<<endl;
 
}
 
}
 
//boost::python::tuple
 
boost::python::object itemTuple;
 
itemTuple = dictlist.pop();
 
while ( itemTuple != NULL )
 
{
 
int nLen = len(itemTuple);
 
string strKey = boost::python::extract<std::string>(itemTuple[0]);
 
string strValue = boost::python::extract<std::string>(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<boost::python::dict>(manager_module->get(),"calldict",inParams);
 
boost::python::object result = boost::python::call_method<boost::python::object>(manager_module->get(),"calldict",inParams);
 
const char* szType1 = result.ptr()->ob_type->tp_name;
 
cout<<"Type : "<<szType1<<endl;
 
 
 
}
 
catch(...)
 
{
 
// delete manager_module;
 
// manager_module = NULL;
 
cout<<"捕获一个异常在calldict方法中"<<endl;
 
}
 
if ( NULL != manager_module )
 
{
 
delete manager_module;
 
manager_module = NULL;
 
}
 
 
 
Py_UNBLOCK_THREADS;
 
Py_END_ALLOW_THREADS;
 
if (!nHold)
 
{
 
PyGILState_Release(gstate); //释放当前线程的GIL
 
}
 
}
 
 
 
 
 
 
 
void callsay(const char* szMessage)
 
{
 
boost::python::handle<>* 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<std::string>(manager_module->get(),"say",szMessage);
 
}
 
catch(...)
 
{
 
delete manager_module;
 
manager_module = NULL;
 
cout<<"捕获一个异常在callsay方法中"<<endl;
 
}
 
delete manager_module;
 
manager_module = NULL;
 
Py_UNBLOCK_THREADS;
 
Py_END_ALLOW_THREADS;
 
if (!nHold)
 
{
 
PyGILState_Release(gstate); //释放当前线程的GIL
 
}
 
}
 
 
 
int _tmain(int argc, _TCHAR* argv[])
 
{
 
char szCommand[20]={0} ;
 
cout<<"请输入指令"<<endl;
 
cin>>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 环境已经初始化"<<endl;
 
continue;
 
}
 
Py_Initialize(); //初始化Python环境
 
if ( !Py_IsInitialized() ) //检测是否初始化成功
 
{
 
cout<<"Python环境初始化失败"<<endl;
 
return NULL;
 
}
 
else
 
{
 
PyEval_InitThreads(); //开启多线程支持
 
int nInit = PyEval_ThreadsInitialized(); //检测线程支持是否开启成功
 
if ( nInit )
 
{
 
PyEval_SaveThread(); //因为调用PyEval_InitThreads成功后,当前线程就拥有了GIL,释放当前线程的GIL,
 
}
 
bInit = true;
 
cout<<"Python环境初始化成功,退出前请使用free指令释放Python环境"<<endl;
 
}
 
}
 
else if ( strcmp("say",szCommand) == 0 )
 
{
 
Check_Init(bInit);
 
std::string msg = "Python Manager";
 
callsay(msg.c_str());
 
}
 
else if ( strcmp("calldict",szCommand) == 0 )
 
{
 
Check_Init(bInit);
 
std::string msg = "Python Manager";
 
calldict();
 
}
 
else if ( strcmp("free",szCommand) == 0 )
 
{
 
int nHold = PyGILState_Check() ; //检测当前线程是否拥有GIL
 
PyGILState_STATE gstate;
 
if ( !nHold )
 
{
 
gstate = PyGILState_Ensure(); //如果没有GIL,则申请获取GIL
 
}
 
Py_Finalize();
 
bInit = false;
 
cout<<"Python解释器环境被释放"<<endl;
 
}
 
else{
 
cout<<"未识别的指令信息"<<endl;
 
 
 
}
 
__next:
 
memset(szCommand,0,20);
 
cin>>szCommand;
 
}
 
system("pause");
 
return 0;
 
}
 
 

ref:(188条消息) C++ 多线程调用Python脚本_追风弧箭的博客-CSDN博客_c++多线程调用python

 

 

 

 

 

 
posted on   DoubleLi  阅读(1025)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2019-01-30 双网卡单IP实现网卡冗余与负载均衡
2018-01-30 live555源码分析----RSTPServer创建过程分析
点击右上角即可分享
微信分享提示