多模块共用python环境管理

news2024/11/5 10:37:31
/*
** Function: 当进程中有多个模块要使用python时,只能公用同一套python环境,此时python环境的初始化和反初始化需要协调,因此提供一个公共模块来对
**                 python环境的初始化和反初始化进行控制,避免各个模块随意修改改python环境导致模块无法并存。
** Usage: 使用方法,让这些使用python的模块都依赖PythonInst,一些初始化和环境配置的动作统一调PythonInst接口完成。
*/

#ifdef PYTHONINST_EXPORTS
#define PYTHONINST_API __declspec(dllexport)
#else
#define PYTHONINST_API __declspec(dllimport)
#endif

#define PYTHONINST_CONST_HANDLE 1

#ifdef __cplusplus
extern "C"
{
#endif
	/*
	** @ Name: Python_Init
	** @ Brief: Python初始化,如果本接口是被首次调用,将检测Python环境是否已初始化,如果未初始化则执行初始化,否则直接返回PYTHONINST_CONST_HANDLE;
	**               如果不是首次调用,则返回PYTHONINST_CONST_HANDLE或者返回一个新建句柄。
	** @ Param: const wchar_t* strPythonHome 设置python工作目录(Python_Init第一次被调用时才生效)。
	** @ Return: void*   返回引用句柄(接口失败时返回NULL)。该句柄是本模块调用初始化接口的句柄凭证,在模块需要退出时,传递给Fini进行解引用。
	*/
	PYTHONINST_API void* __stdcall Python_Init(const wchar_t* strPythonHome);
	/*
	** @ Name: Python_Fini
	** @ Brief: Python反初始化,如果hHandle为有效句柄,则引用计数减少(减到0时将销毁Python解释器),如果句柄无效则不做任何处理。
	** @ Param: void* hHandle 由Python_Init创建得到的句柄,hHandle可能是PYTHONINST_CONST_HANDLE / 有效句柄 / NULL
	** @ Return: void
	*/
	PYTHONINST_API void __stdcall Python_Fini(void* hHandle);
	/*
	** @ Name: Python_SetGlobalPyHome
	** @ Brief: 设置全局的Python工作空间,等同于Python_Init传入的参数,在Python_Init首次被调用之前传入有效。
	** @ Param: const wchar_t* strPath Python工作空间路径,设为NULL时,表示使用默认路径作为Python工作空间。
	** @ Return: int 返回0表示成功,否则表示错误码。
	*/
	PYTHONINST_API int __stdcall Python_SetGlobalPythonHome(const wchar_t* strPath);
	/*
    ** @ Name: Python_AppendPythonHome
    ** @ Brief: 追加Python工作空间路径。
    ** @ Param: const wchar_t* strPath Python工作空间路径。
    ** @ Return: int 返回0表示成功,否则表示错误码。
    */
	PYTHONINST_API int __stdcall Python_AppendPythonHome(const wchar_t* strPath);
	/*
	** @ Name: Python_SetGlobalInstance
	** @ Brief: 使能/关闭 全局Python实例功能(Python_Init首次被调用之前传入有效)。如果开启,则在Python_Init被调用时,额外创建一个全局的伴生
	**                Handle,即便所有的实例都被Fini(全局Handle除外),python解释器因为全局实例未销毁从而继续被保留,其他模块还可以继续使用python解释器。
	** @ Param: int enable 是否开启,1表示开启,0表示关闭。
	** @ Return: int 返回0表示成功,否则表示错误码。
	*/
	PYTHONINST_API int __stdcall Python_SetGlobalInstanceEnable(int enable);

	/*
	** @ Name: Python_GetGlobalInstance
	** @ Brief: 获取全局Python实例句柄。全局实例句柄在运行过程中会失效,外部不要做缓存,需要时来查询一下即可。
	** @ Param: void
	** @ Return: void* 全局实例句柄。
	*/
	PYTHONINST_API void* __stdcall Python_GetGlobalInstance();

#ifdef __cplusplus
}
#endif



#include <python.h>
#include <map>
#include <mutex>
#include <memory>
#define PYTHONINST_EXPORTS
#include "PythonInst.h"

class PythonInstance
{
public:
	static PythonInstance* CreateInstance(const wchar_t* strPythonHome);
	static void DestroyInstance(PythonInstance* handle);
	~PythonInstance();
	static int PreparePythonHome(const wchar_t* strPythonHome);
	static int SetGlobalInstEnable(int bEnable);
    static int SetPythonHome(const wchar_t* strPythonHome, bool append=false);
	static PythonInstance* GetGlobalInst();
private:
	static std::mutex s_PyInitMutex;
	static std::map<void*, std::shared_ptr<PythonInstance>> s_mapInst;
	static std::wstring s_strPythonHome;
	static int s_bGloobalInst;
	static PythonInstance* s_GlobalHandle;

private:
	PythonInstance();
	static void SetPythonGlobal();
	
	static int CreateGlobalInstWhenEnable();
	static void ResetIfGlobalInst(void* handle);

};

class PythonThreadLocker // python lock guard
{
public:
	PythonThreadLocker() 
	{
		//if (PyGILState_Check() != 1)
			state = PyGILState_Ensure();
	}
	~PythonThreadLocker() 
	{
	    //if(state != PyGILState_UNLOCKED)
			PyGILState_Release(state);
	}
private:
	PyGILState_STATE state = PyGILState_UNLOCKED;
};


typedef std::shared_ptr<PythonInstance> PythonInstancePtr;

std::mutex PythonInstance::s_PyInitMutex;
std::map<void*, PythonInstancePtr> PythonInstance::s_mapInst;
std::wstring PythonInstance::s_strPythonHome = L"";
int PythonInstance::s_bGloobalInst = 0;
PythonInstance* PythonInstance::s_GlobalHandle = NULL;

PythonInstance::PythonInstance()
{}

PythonInstance::~PythonInstance()
{}

void PythonInstance::SetPythonGlobal()
{
	if (0 != s_strPythonHome.compare(L""))
	{
		const wchar_t* envStr = Py_GetPythonHome();
		wchar_t env[1024] = {};
		if (envStr != NULL)
		{
			_snwprintf_s(env, 1024,1023, L"%s;%s", envStr, s_strPythonHome.c_str());
			Py_SetPythonHome(env);
		}
		else
			Py_SetPythonHome(s_strPythonHome.c_str());
	}
}

int PythonInstance::SetPythonHome(const wchar_t* strPythonHome, bool append )
{
	if (NULL != strPythonHome && Py_IsInitialized())
	{
		PythonThreadLocker lock;
		if (append) 
		{
			const wchar_t* envStr = Py_GetPythonHome();
			if (envStr != NULL)
			{
				wchar_t env[1024] = {};
				_snwprintf_s(env, 1024, 1023, L"%s;%s", envStr, strPythonHome);
				Py_SetPythonHome(env);
			}
			else
				Py_SetPythonHome(strPythonHome);
		}
		else
			Py_SetPythonHome(strPythonHome);
		return 0;
	}
	return -1;
}

int PythonInstance::CreateGlobalInstWhenEnable()
{
	if (0 != s_bGloobalInst)
	{
		std::shared_ptr<PythonInstance> pInst(new PythonInstance());
		if (nullptr == pInst)
		{
			return -1;
		}
		s_mapInst[pInst.get()] = pInst;
		ResetIfGlobalInst(s_GlobalHandle);
		s_GlobalHandle = pInst.get();
	}
	return 0;
}

void PythonInstance::ResetIfGlobalInst(void* handle)
{
	if (handle == s_GlobalHandle)
	{
		auto it = s_mapInst.find(s_GlobalHandle);
		if (it != s_mapInst.end())
			s_mapInst.erase(s_GlobalHandle);
        s_GlobalHandle = NULL;
	}
}

#define GLOBAL_MODE_RETURN() {if(s_GlobalHandle != NULL) \
{\
     auto it = s_mapInst.find(s_GlobalHandle); \
     if(it != s_mapInst.end()) \
     {\
        return (PythonInstance*)PYTHONINST_CONST_HANDLE;\
	 }\
}\
}

PythonInstance* PythonInstance::CreateInstance(const wchar_t* strPythonHome)
{
	std::lock_guard<std::mutex> cLock(s_PyInitMutex);
	PythonInstancePtr pInst = PythonInstancePtr(new PythonInstance);
	if (NULL == pInst)
		return NULL;
	if (0 == s_mapInst.size())
	{

		if (Py_IsInitialized())
		{
			// 这种情况说明模块没有调用本库的Python_Init接口完成python初始化,而是在其他地方对python环境初始化了,本
			// 库不负责引用计数管理,直接返回(避免Destroy接口内部在销毁句柄时报错,返回非NULL)
			return (PythonInstance*)PYTHONINST_CONST_HANDLE;
		}
		// 先进行全局设置
		SetPythonGlobal();
		//如果入参提供了工作空间,则更新为用户指定的工作空间
		SetPythonHome(strPythonHome);
		// 初始化
		Py_Initialize();

		if (!Py_IsInitialized())
			return NULL;
		/*if (!PyEval_ThreadsInitialized())  // depreacted from python 3.7 使能多线程,自3.7以后,该接口功能合并到了Py_Initialize
		{
			PyEval_InitThreads();
		}*/
		CreateGlobalInstWhenEnable();//如果开启了全局实例,则额外创建一个全局伴生实例
	}
	GLOBAL_MODE_RETURN();
	s_mapInst[pInst.get()] = pInst;
	return pInst.get();
}

void PythonInstance::DestroyInstance(PythonInstance* handle)
{
	std::lock_guard<std::mutex> cLock(s_PyInitMutex);
	if (NULL == handle)
		return;
	auto it = s_mapInst.find(handle);
	if (it != s_mapInst.end())
	    s_mapInst.erase(it);
	ResetIfGlobalInst(handle);
	if (0 != s_mapInst.size())
		return;
	try {
		PyGILState_STATE gilOk=PyGILState_Ensure();
		Py_Finalize();
		s_GlobalHandle = NULL;
		//PyGILState_Release(gilOk);//no need to release, because things has been deinitialized by Py_Finalize.
	}
	catch (...)
	{
		PyErr_SetString(PyExc_RuntimeError,"PythonInstance Deinitialization Failed.");
	}
}

int PythonInstance::PreparePythonHome(const wchar_t* strPythonHome)
{
	std::lock_guard<std::mutex> cLock(s_PyInitMutex);
	if (NULL == strPythonHome)
		s_strPythonHome = L"";
	else
		s_strPythonHome = strPythonHome;
	return 0;
}

int PythonInstance::SetGlobalInstEnable(int bEnable)
{
	std::lock_guard<std::mutex> cLock(s_PyInitMutex);
	s_bGloobalInst = bEnable;
	return 0;
}

PythonInstance* PythonInstance::GetGlobalInst()
{
	std::lock_guard<std::mutex> cLock(s_PyInitMutex);
	return s_GlobalHandle;
}


PYTHONINST_API void* __stdcall Python_Init(const wchar_t* strPythonHome)
{
	return PythonInstance::CreateInstance(strPythonHome);
}

PYTHONINST_API void __stdcall Python_Fini(void* hHandle)
{
	return PythonInstance::DestroyInstance((PythonInstance*)hHandle);
}

PYTHONINST_API int __stdcall Python_SetGlobalPythonHome(const wchar_t* strPath)
{
	return PythonInstance::PreparePythonHome(strPath);
}

PYTHONINST_API int __stdcall Python_AppendPythonHome(const wchar_t* strPath)
{
	return PythonInstance::SetPythonHome(strPath,true);
}

PYTHONINST_API int __stdcall Python_SetGlobalInstanceEnable(int enable)
{
	return PythonInstance::SetGlobalInstEnable(enable);
}

PYTHONINST_API void* __stdcall Python_GetGlobalInstance()
{
	return PythonInstance::GetGlobalInst();
}

  众所周知,cc++中调用任何python 接口前,必须对python 解释器进行初始化,并进行必要的环境配置,这些初始化和环境配置是全局生效的,如果这些接口放到各个模块内部执行,将不利于管理,所以需要提炼一个公共模块,对python的全局环境进行初始化和配置,增加防护措施,以使各个模块和谐共存。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2232513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

word及Excel常见功能使用

最近一直在整理需规文档及表格&#xff0c;Word及Excel需要熟练使用。 Word文档 清除复制过来的样式 当复制文字时&#xff0c;一般会带着字体样式&#xff0c;此时可选中该文字 并使用 ctrlshiftN 快捷键进行清除。 批注 插入->批注&#xff0c;选中文本 点击“批注”…

【Linux 27】HTTP 协议中的 cookie 和 session

文章目录 &#x1f308;一、Cookie 的相关概念⭐ 1. Cookie 的概念⭐ 2. Cookie 的工作原理⭐ 3. Cookie 的分类⭐ 4. Cookie 的用途⭐ 5. Cookie 设置的基本格式⭐ 6. Cookie 设置时的注意事项⭐ 7. Cookie 的生命周期⭐ 8. Cookie 的安全性问题 &#x1f308; 二、Session 的…

SQL用一个字段查询出数据的交集

出个SQL查询的题吧&#xff0c;有兴趣的可以看看 1、问题 下面有一份数据&#xff08;图1&#xff09;&#xff0c;由两部分组成&#xff1a;分析数据和基准数据 分析数据标识列为1&#xff0c;基准数据标识列为1&#xff0c;两字段0,1互斥 要求&#xff1a;按找出两部分数据…

后端java——如何为你的网页设置一个验证码

目录 1、工具的准备 2.基本方法 3.实现类 4.实践 HTML文件&#xff1a; Java文件1:创建验证码 Java文件2:验证验证码 本文通过HUTOOL实现&#xff1a;Hutool参考文档Hutool&#xff0c;Java工具集https://hutool.cn/docs/#/ 1、工具的准备 如果我们通过hutool来实现这个…

【Python单元测试】pytest框架单元测试 配置 命令行操作 测试报告 覆盖率

单元测试&#xff08;unit test&#xff09;&#xff0c;简称UT。本文将介绍在Python项目中&#xff0c;pytest测试框架的安装&#xff0c;配置&#xff0c;执行&#xff0c;测试报告与覆盖率 pytest简介 pytest是一款流行的&#xff0c;简单易上手的单元测试框架&#xff0c;…

HTMLCSS:呈现的3D树之美

效果演示 这段代码通过HTML和CSS创建了一个具有3D效果的树的图形&#xff0c;包括分支、树干和阴影&#xff0c;通过自定义属性和复杂的变换实现了较为逼真的立体效果。 HTML <div class"container"><div class"tree"><div class"…

练习LabVIEW第三十八题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第三十八题&#xff1a; 创建一个VI&#xff0c;实现对按钮状态的指示和按钮“按下”持续时间简单计算功能&#xff0c;按…

HomeAssistant自定义组件学习-【二】

#要说的话# 前面把中盛科技的控制器组件写完了。稍稍熟悉了一些HA&#xff0c;现在准备写窗帘控制组件&#xff0c;构想的东西会比较多&#xff0c;估计有些难度&#xff0c;过程会比较长&#xff0c;边写边记录吧&#xff01; #设备和场景环境# 使用的是Novo的电机&#xf…

Linux脚本数组与字符串

文章目录 打印数组与长度数组遍历数组赋值下标索引访问切片追加()删除关联数组(像map)字符串字符拼接截取子串字符串长度字符串替换模式匹配截取分割字符串大小写转换 打印数组与长度 ${arrayName[*]} 打印数组${arrayName[]} 打印数组${#arrayName[*]} 打印数组长度${#arrayN…

闯关leetcode——3289. The Two Sneaky Numbers of Digitville

大纲 题目地址内容 解题代码地址 题目 地址 https://leetcode.com/problems/the-two-sneaky-numbers-of-digitville/description/ 内容 In the town of Digitville, there was a list of numbers called nums containing integers from 0 to n - 1. Each number was suppos…

#Jest进阶知识:整合 webpack 综合练习

这一小节&#xff0c;我们来做一个综合的练习&#xff0c;该练习会整合&#xff1a; typescriptwebpackjest 准备工作 首先创建项目目录&#xff0c;通过 npm init -y 进行初始化。 整个项目我们打算使用 typescript 进行开发&#xff0c;因此需要安装 typescript npm i t…

MATLAB——矩阵操作

内容源于b站清风数学建模 数学建模清风老师《MATLAB教程新手入门篇》https://www.bilibili.com/video/BV1dN4y1Q7Kt/ 目录 1.MATLAB中的向量 1.1向量创建方法 1.2向量元素的引用 1.3向量元素修改和删除 2.MATLAB矩阵操作 2.1矩阵创建方法 2.2矩阵元素的引用 2.3矩阵…

原生鸿蒙应用市场开发者服务的技术解析:从集成到应用发布的完整体验

文章目录 引言一、鸿蒙原生应用的高效开发二、用户隐私保护&#xff1a;安全访问管理三、开发者实用工具&#xff1a;应用分析与A/B测试四、应用审核与分发&#xff1a;快速上线4.1 应用加密&#xff1a;保护代码安全4.2 自动化测试与检测前移&#xff1a;提升应用质量 五、结语…

基于SSM+微信小程序的社团登录管理系统(社团1)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 2、项目技术 3、开发环境 4、功能介绍 1、项目介绍 基于SSM微信小程序的社团登录管理系统实现了管理员及社团、用户。 1、管理员实现了首页、用户管理、社团管理、社团信息管理、社…

虚拟化环境中的精简版 Android 操作系统 Microdroid

随着移动设备的普及和应用场景的多样化&#xff0c;安全性和隐私保护成为了移动操作系统的重要课题。Google推出的Microdroid&#xff0c;是一个专为虚拟化环境设计的精简版Android操作系统&#xff0c;旨在提供一个安全、隔离的执行环境。本文将详细介绍Microdroid的架构、功能…

手动搭建 Java Web 环境

操作场景 本文档介绍如何在 Linux 操作系统的腾讯云云服务器&#xff08;CVM&#xff09;上手动搭建 Java Web 环境。 进行手动搭建 Java Web 环境&#xff0c;您需要熟悉 Linux 命令&#xff0c;例如 CentOS 环境下通过 YUM 安装软件 等常用命令&#xff0c;并对所安装软件使…

WPF+MVVM案例实战与特效(二十四)- 粒子字体效果实现

文章目录 1、案例效果2、案例实现1、文件创建2.代码实现3、界面与功能代码3、总结1、案例效果 提示:这里可以添加本文要记录的大概内容: 2、案例实现 1、文件创建 打开 Wpf_Examples 项目,在 Views 文件夹下创建窗体界面 ParticleWindow.xaml,在 Models 文件夹下创建粒子…

「Mac畅玩鸿蒙与硬件18」鸿蒙UI组件篇8 - 高级动画效果与缓动控制

高级动画可以显著提升用户体验&#xff0c;为应用界面带来更流畅的视觉效果。本篇将深入介绍鸿蒙框架的高级动画&#xff0c;包括弹性动画、透明度渐变和旋转缩放组合动画等示例。 关键词 高级动画弹性缓动自动动画缓动曲线 一、Animation 组件的高级缓动曲线 缓动曲线&#…

Golang--数组、切片、映射

1、数组 1.1 数组类型 var 数组名 [数组大小]数据类型 package main import "fmt"func main(){//1、定义一个数组var arr1 [5]intarr1[0] 100arr1[1] 200fmt.Println(arr1) //[100 200 0 0 0] } 1.2 数组的初始化方式 package main import "fmt" func …

Android音频进阶之PCM设备创建(九十三)

简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+…