背景

目前我主要负责的一个项目是一个 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

用 C# 来守护 Python 进程相关推荐

  1. 创建一个守护进程来监听服务进程的异常状态_用 C# 来守护 Python 进程

    (给DotNet加星标,提升.Net技能) 转自:hippieZhoucnblogs.com/hippieZhou/p/11504552.html 背景 主要负责的一个项目是一个 C/S 架构的客户端 ...

  2. Python进程、线程、协程详解

    进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配.任务的调度. ...

  3. Python 进程 Process 模块 - Python零基础入门教程

    目录 一.Python 进程 Process 简介 二.Python 进程 Process 模块 三.Python 进程 Process 函数介绍 四.Python 进程 Process 使用 五.P ...

  4. python/进程线程的总结

    python/进程线程的总结 一.进程和线程的描述:进程:最小的资源管理单位线程:最小的执行单位执行一个进程时就默认执行一个线程(主线程)进程和线程的工作方式:串行:假如共有A.B.C任务, 串行的执 ...

  5. python 进程间同步_python之路29 -- 多进程与进程同步(进程锁、信号量、事件)与进程间的通讯(队列和管道、生产者与消费者模型)与进程池...

    所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了.至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠 ...

  6. python 进程,线程,协程篇

    python 进程,线程,协程篇 ssh 线程 进程 线程,进程区别 threading 模块,两种调用方式 python GIL全局解释器锁(Global Interpreter Lock) Joi ...

  7. 使用Supervisor守护Uwsgi进程,为你的Django网站保驾护航

    1.了解Supervisor Supervisor是一款运行在类Unix系统(如Linux.MacOS系统)上的进程管理软件,基于Python开发.通过它我们可以很方便的管理服务器上的各种程序的进程, ...

  8. python进程和线程_Python进程与线程知识

    好程序员Python 培训分享进程与线程知识 , Python 开发语言现在已经是被大家非常看中的编程语言了,本篇文章给读者们分享一下 Python 进程与线程知识小结,本篇文章具有一定的参考借鉴价值 ...

  9. Python 进程之间共享数据(全局变量)

    Python 进程之间共享数据(全局变量) 进程之间共享数据(数值型): import multiprocessing def func(num): num.value=10.78 #子进程改变数值的 ...

最新文章

  1. git如何移除某文件的版本控制
  2. ASPNET服务器控件之一
  3. 微信小程序asp服务器架设,asp写的微信小程序支付demo-服务器端是asp+mdb的
  4. 再见,Python。你好,Go 语言
  5. sklearn 神经网络_sklearn中的数据预处理和特征工程
  6. 03JavaScript程序设计修炼之道-2019-06-20_20-31-49
  7. [HAOI2015]T2
  8. python文件之间如何互相通信_不同的類和.py文件之間的python通信
  9. python学习之旅(入门)
  10. mysql swarm_【Docker】 Swarm简单介绍
  11. Missing session attribute 'user' of type List 解决办法
  12. Atitit.异常的设计原理与 策略处理 java 最佳实践 p93
  13. 【单目标优化求解】基于matlab非线性权重的自适应鲸鱼算法求解单目标优化问题(NWAWOA)【含Matlab源码 1665期】
  14. Java函数、参数及传参方式详解
  15. windows7 x64x86专业纯净版(usb3.0_nvme)2019.12.17
  16. 管理Linux系统中的进程
  17. 微信小程序系列——js遍历数组,微信小程序数组的遍历,forEach
  18. 西普实验吧ctf-web-程序逻辑问题(代码审计)
  19. 《暮光之城●破晓(下)》
  20. 第二次作业——结对项目之需求分析与原型设计

热门文章

  1. HDU3363_贪心
  2. 使用PowerShell配置Microsoft Teams
  3. plex实现流媒体服务器_Plex继续远离服务器,提供网络节目
  4. Netty1:初识Netty
  5. Java并发包--线程池框架
  6. Mysql 的子查询
  7. 用户反馈KB3189866累积更新出现卡在95%进度情况
  8. php的几种运行模式CLI、CGI、FastCGI、mod_php
  9. Windows用户安全小技巧
  10. 分布式拒绝服务攻击(DDoS)原理及防范