用 C# 来守护 Python 进程

640?wx_fmt=png

背景

目前我主要负责的一个项目是一个 C/S 架构的客户端开发,前端主要是通过 WPF 相关技术来实现,后端是通过 Python 来实现,前后端的数据通信则是通过 MQ 的方式来进行处理。由于 Python 进程是需要依赖客户端进程来运行,为了保证后端业务进程的稳定性,就需要通过一个 守护进程 来守护 Python 进程,防止其由于未知原因而出现进程退出的情况。这里简单记录一下我的一种实现方式。

实现

对于我们的系统而言,我们的 Python 进程只允许存在一个,因此,对应的服务类型要采用单例模式,这一部分代码相对简单,就直接贴出来了,示例代码如下所示:

public partial class PythonService
{private static readonly object _locker = new object();private static PythonService _instance;public static PythonService Current{get{if (_instance == null){lock (_locker){if (_instance == null){_instance = new PythonService();}}}return _instance;}}private PythonService(){}
}

创建独立进程

由于后端的 Python 代码运行需要安装一些第三方的扩展库,所以为了方便,我们采用的方式是总结将 python 安装文件及扩展包和他们的代码一并打包到我们的项目目录中,然后创建一个 Python 进程,在该进程中通过设置环境变量的方式来为 Python 进程进行一些环境配置。示例代码如下所示:

public partial class PythonService
{private string _workPath => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "scripts");private string _pythonPath => Path.Combine(_workPath, "python27");private bool isRunning = false;private int taskPID = -1;public void Start(){taskPID = CreateProcess();isRunning = taskPID != -1;var msg = isRunning ? "服务启动成功..." : "服务启动失败...";Trace.WriteLine(msg);}public void Stop(){KillProcessAndChildren(taskPID);isRunning = false;taskPID = -1;}private int CreateProcess(){KillProcessAndChildren(taskPID);int pid = -1;var psi = new ProcessStartInfo(Path.Combine(_pythonPath, "python.exe")){UseShellExecute = false,WorkingDirectory = _workPath,ErrorDialog = false};psi.CreateNoWindow = true;var path = psi.EnvironmentVariables["PATH"];if (path != null){var array = path.Split(new[] { ';' }).Where(p => !p.ToLower().Contains("python")).ToList();array.AddRange(new[] { _pythonPath, Path.Combine(_pythonPath, "Scripts"), _workPath });psi.EnvironmentVariables["PATH"] = string.Join(";", array);}var ps = new Process { StartInfo = psi };if (ps.Start()){pid = ps.Id;}return pid;}private static void KillProcessAndChildren(int pid){if (pid <= 0){return;}ManagementObjectSearcher searcher = new ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pid);ManagementObjectCollection moc = searcher.Get();foreach (ManagementObject mo in moc){KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));}try{Process proc = Process.GetProcessById(pid);proc.Kill();}catch (ArgumentException){}catch (Win32Exception){}}
}

这里有一点需要注意一下,建议使用 PID 来标识我们的 Python 进程,因为如果你使用进程实例或其它方式来对当前运行的进程设置一个引用,当该进程出现一些未知退出,这个时候你通过哪个引用来进行相关操作是会出问题的。

创建守护进程

上面我们的通过记录当前正在运行的进程的 PID 来标识我们的进程,那对应守护进程,我们就可以通过进程列表查询的方式来进行创建,在轮询的过程中,如果未找到对应 PID 的进程则表明该进程已经退出,需要重新创建该进程,否则就不执行任何操作,示例代码如下所示:

public partial class PythonService
{private CancellationTokenSource cts;private void StartWatch(CancellationToken token){Task.Factory.StartNew(() =>{while (!token.IsCancellationRequested){var has = Process.GetProcesses().Any(p => p.Id == taskPID);Trace.WriteLine($"MQ状态:{DateTime.Now}-{has}");if (!has){taskPID = CreateProcess(_reqhost, _subhost, _debug);isRunning = taskPID > 0;var msg = isRunning ? "MQ重启成功" : "MQ重启失败,等待下次重启";Trace.WriteLine($"MQ状态:{DateTime.Now}-{msg}");}Thread.Sleep(2000);}}, token);}
}

这里我使用的是 Thread.Sleep(2000) 方式来继续线程等待,你也可以使用 await Task.Delay(2000,token),但是使用这种方式在发送取消请求时会产生一个 TaskCanceledException 的异常。所以为了不产生不必要的异常信息,我采用第一种解决方案。

接着,完善我们的 Start 和 Stop 方法,示例代码如下所示:

public void Start()
{taskPID = CreateProcess();isRunning = taskPID != -1;if (isRunning){cts = new CancellationTokenSource();StartWatch(cts.Token);}var msg = isRunning ? "服务启动成功..." : "服务启动失败...";Trace.WriteLine(msg);
}public void Stop()
{cts?.Cancel(false);cts?.Dispose();KillProcessAndChildren(taskPID);taskPID = -1;isRunning = false;
}

最后,上层调用就相对简单一下,直接调用 Start 方法和 Stop 方法即可。

总结

在我们的实际项目代码中,PythonService 的代码要比上面的代码稍微复杂一些,我们内部还添加了一个 MQ 的 消息队列。所以为了演示方便,我这里只列出了和本文相关的核心代码,在具体的使用过程中,可以依据本文提供的一种实现方法来进行加工处理。

相关参考

  • Kill a one-file Python process in C#

  • 用c#实现通用守护进程

原文链接:https://www.cnblogs.com/hippieZhou/p/11504552.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

640?wx_fmt=jpeg

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

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

相关文章

针对媒体不实报道误导大众--抹黑C#工资垫底

近注意到一些媒体故意抹黑C# 工资垫底,参见 https://www.toutiao.com/i6741889572931633668/:通过搜索引擎搜索《编程语言薪酬排行&#xff1a;Python薪资最高&#xff0c;Java第二&#xff0c;C# 垫底》&#xff1a;早在2018年就出现这样的标题内容&#xff0c;还是CSDN公众号…

程序员你写的代码,被人挖出了黑产

事件经过看了微博上发表转发1000 、点赞1000次的吐槽陕西省的普通话成绩查询网站代码的微博&#xff0c;后来知乎上又有20万的阅读量这个话题的提问。最终结案这并不是真的陕西省普通话成绩查询网的网址&#xff0c;只不过是和官方查询一样的界面&#xff0c;李鬼”网站&#x…

Java垃圾回收日志解析

1.开启垃圾回收日志 在运行一个java程序时可以在命令行中加入相应的JVM垃圾回收参数&#xff0c;获取程序运行时详细的垃圾回收日志信息。以下是一些大概的参数&#xff1a; -XX:PrintGC与-verbose:gc 这两个命令效果都是一样&#xff0c;打印最基本的回收信息 -XX:PrintGCDe…

感谢有你们,架构师修行之路!

感谢有你们转眼马上就十月一了&#xff0c;听说今年的阵势非常强大&#xff0c;菜菜虽然身在北京&#xff0c;但是可能也目睹不了这个激动时刻了。自从2018年年底决定开始写公众号以来&#xff0c;几乎每个周末都在构思文章&#xff0c;撰写文章。关注公众号的老粉丝应该知道&a…

自定义构建基于.net core 的基础镜像

先说一个问题首先记录一个问题&#xff0c;今天在用 Jenkins 构建项目的时候突然出现包源的错误&#xff1a;/usr/share/dotnet/sdk/2.2.104/NuGet.targets(114,5): error : Unable to load the service index for source https: /usr/share/dotnet/sdk/2.2.104/NuGet.targets(…

操作系统——缓冲区溢出

一、缓冲区溢出介绍 1988年&#xff0c;世界上第一个缓冲区溢出攻击–Morris蠕虫在互联网上泛滥&#xff0c;短短一夜的时间全世界6000多台网络服务器瘫痪或半瘫痪&#xff0c;不计其数的数据和资料被毁。造成一场损失近亿美元的空前大劫难&#xff01; 那么&#xff0c;缓冲…

从壹开始学习 NetCore 新篇章 ║ Blog.Core 开发社之招募计划书

宫哈喽大家好&#xff0c;国庆马上就要来了&#xff0c;在新的第四季度来临之际&#xff0c;祝大家年末能顺顺利利&#xff0c;解决所有的难题。大家可能从我的标题里也能看的出来&#xff0c;老张又要耍花样&#xff0c;搞事情了&#xff0c;近来随着 netcore 3.0 的正式推出&…

操作系统——内存管理——分段和分页

一、 物理地址和逻辑地址 物理地址&#xff1a;加载到内存地址寄存器中的地址&#xff0c;内存单元的真正地址。在前端总线上传输的内存地址都是物理内存地址&#xff0c;编号从0开始一直到可用物理内存的最高端。这些数字被北桥(Nortbridge chip)映射到实际的内存条上。物理地…

Kubernetes攻略之新手上路

在公有云、私有云和混合云的环境中&#xff0c;Kubernetes已经成为规模化部署容器应用的事实标准。最大的公有云平台AWS、谷歌云、Azure、IBM云和Oracle云目前都提供Kubernetes的管理服务&#xff08;Managed Services&#xff09;。各大互联网公司也开始将服务部署到Kubernete…

操作系统——深入理解虚拟内存机制

本文来自&#xff1a;https://www.jianshu.com/p/13e337312651 概述 现代操作系统了提供了一种对主存的抽象概念&#xff0c;叫做虚拟内存。它为每个进程提供了一个非常大的&#xff0c;一致的和私有的地址空间。虚拟内存提供了以下的三个关键能力&#xff1a; 它为所有进程提…

dotNET Core 中怎样操作 AD?

做企业应用开发难免会跟 AD 打交道&#xff0c;在之前的 dotNET FrameWork 时代&#xff0c;通常使用 System.DirectoryServices 的相关类来操作 AD &#xff0c;在 dotNET Core 中没有这个命名空间&#xff0c;在张善友大佬的推荐下&#xff0c;知道了 Novell.Directory.Ldap。…

操作系统——页面置换算法

一、页面置换算法简介 操作系统将内存按照页的进行管理&#xff0c;在需要的时候才把进程相应的部分调入内存。当产生缺页中断时&#xff0c;需要选择一个页面写入。如果要换出的页面在内存中被修改过&#xff0c;变成了“脏”页面&#xff0c;那就需要先写会到磁盘。页面置换…

在树莓派4上安装 .NET Core 3.0 运行时及 SDK

点击上方蓝字关注“汪宇杰博客”导语我最近买了个树莓派4&#xff0c;4GB内存高富帅配置&#xff0c;并安装了官方操作系统Raspbian。今天我成功运行了一个ASP.NET Core 3.0 应用程序。我们来看看怎么弄的吧~ARM32 还是 ARM64?需要说明的是&#xff0c;目前无法在树莓派 4 上运…

.NET生成漂亮桌面背景

前言一天&#xff0c;我朋友指着某某付费软件对我说&#xff0c;这个东西不错&#xff0c;每天生成一张桌面背景&#xff0c;还能学英语&#xff08;放置名人名言和翻译&#xff09;&#xff01;我说&#xff0c;这东西搞不好我也能做&#xff0c;然后朋友说&#xff0c;“如果…

Mysql 执行流程

mysql执行一个查询的过程&#xff0c;到底做了些什么&#xff1a; 客户端发送一条查询给服务器&#xff1b;服务器先检查查询缓存&#xff0c;如果命中了缓存&#xff0c;则立刻返回存储在缓存中的结果。否则进入下一阶段。服务器段进行SQL解析、预处理&#xff0c;在优化器生成…

Autofac的AOP面向切面编程研究

我的理解是 把系统性的编程工作封装起来 》我给这个取个名字叫 “Aspect”&#xff0c;然后通过AOP技术把它切进我们的业务逻辑代码 》 “业务“这样的好处&#xff1a;“Aspect” 和 “业务” 相互独立&#xff0c;既可以让“业务” 用到了 “Aspect” 又让2者互相独立不耦合&…

计算机网络原理梳理丨清晰认识 TCP/IP 协议,图解秒懂!

作者&#xff1a;MobMsg&#xff0c;资深全端工程师一枚&#xff0c;架构师社区合伙人&#xff01;TCP/IP 协议族Internet 的核心协议就是 TCP/IP&#xff0c;广泛应用于局域网和广域网&#xff0c;目前已有20年发展史&#xff0c;是现用国际通行标准。TCP/IP 是个协议族&#…

MySQL 覆盖索引、最左前缀原则、索引下推

1、覆盖索引 1.1 概念 索引是高效找到行的一个方法&#xff0c;当能通过检索索引就可以读取想要的数据&#xff0c;那就不需要再到数据表中读取行了。如果一个索引包含了&#xff08;或覆盖了&#xff09;满足查询语句中字段与条件的数据就叫做覆盖索引。 1.2 判断标准 使用…

Entity Framework Core生成的存储过程在MySQL中需要进行处理及PMC中的常用命令

在使用Entity Framework Core生成MySQL数据库脚本&#xff0c;对于生成的存储过程&#xff0c;在执行的过程中出现错误&#xff0c;需要在存储过程前面添加delimiter //附&#xff1a;可以使用Visual Studio中的程序包管理器控制台执行Entity Framework Core中的迁移命令。PMC …

Exceptionless 5.0.0本地Docker快速部署介绍

在之前我有专门写两篇文章介绍过Exceptionless这款开源日志项目的使用和部署&#xff0c;但是当时是基于4.1.0版本&#xff08;2017年的release&#xff09;&#xff0c;时隔两年多Exceptionless也推出了5.0.0版本。&#xff08;1&#xff09;&#xff08;2&#xff09;01—关于…