using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; namespace SHH.ProcessLaunchers { /// /// 进程管理器核心接口 /// public interface IProcessManager { /// /// 注册一个要管理的进程配置 /// /// 进程配置对象 void Register(ProcessConfig config); /// /// 启动指定名称的进程 /// /// 配置中定义的 Name void Start(string name); /// /// 启动所有已注册的进程 /// Task StartAllAsync(); /// /// 停止指定进程 (优雅或强制) /// /// 进程名称 void Stop(string name); /// /// 停止所有进程 /// void StopAll(); /// /// 获取指定进程的资源监控复位接口 /// /// 进程名称 void ResetGuard(string processName); /// /// 获取所有进程的实时状态快照 /// List GetSnapshot(); // --- 事件定义 --- /// /// 当接收到子进程的标准输出或错误流时触发 /// event EventHandler OnOutputReceived; /// /// 当进程生命周期状态发生变化时触发 /// event EventHandler OnStateChanged; } public class MemoryGuard : IResourceGuard { public string Name => "MemoryGuard"; private readonly long _warningBytes; private readonly long _criticalBytes; private readonly TimeSpan _alertDuration; // 持续时间阈值 (如 3分钟) // --- 内部状态 --- private DateTime? _firstOverLimitTime; // 第一次检测到超限的时间 private bool _isAlertLatched = false; // 是否已经报过警 (自锁) /// /// 智能内存哨兵 /// /// 警告阈值 /// 熔断阈值 /// 必须持续超限多少分钟才报警 public MemoryGuard(int warningMb, int criticalMb, int durationMinutes = 3) { _warningBytes = (long)warningMb * 1024 * 1024; _criticalBytes = (long)criticalMb * 1024 * 1024; _alertDuration = TimeSpan.FromMinutes(durationMinutes); } public GuardResult Check(Process process, out string reason) { reason = null; try { process.Refresh(); long currentUsage = process.WorkingSet64; // 1. 优先检查 Critical (熔断线) // 逻辑:熔断涉及生死,不需要防抖,也不受“已报警”锁定的限制。 // 哪怕用户标记了已处置,只要内存爆了,必须重启。 if (currentUsage > _criticalBytes) { reason = $"[严重] 内存 {FormatSize(currentUsage)} > 熔断线 {FormatSize(_criticalBytes)} (立即执行管控)"; // 重启后,物理进程会变,下一次 Check 会是新进程,状态建议在重启时由外部重置, // 或者这里不重置,依靠 ProcessManager 重启后重新创建 Guard 实例。 return GuardResult.Critical; } // 2. 检查 Warning (警告线) - 包含防抖和自锁逻辑 if (currentUsage > _warningBytes) { // A. 如果已经报过警 (已锁定),则不再报,保持沉默 if (_isAlertLatched) { return GuardResult.Normal; } // B. 如果是刚发现超限,记录时间 if (_firstOverLimitTime == null) { _firstOverLimitTime = DateTime.Now; // 这里可以选做:记录一条 Info 日志,告诉用户"正在观察中" // reason = $"内存超限 {FormatSize(currentUsage)},开始计时观察..."; return GuardResult.Normal; // 暂时不报 Warning } // C. 检查持续时间 var duration = DateTime.Now - _firstOverLimitTime.Value; if (duration >= _alertDuration) { // 满足持续时间 -> 触发报警并锁定 _isAlertLatched = true; _firstOverLimitTime = null; // 计时归零 reason = $"[报警] 内存 {FormatSize(currentUsage)} > 阈值 {FormatSize(_warningBytes)} 且持续超过 {_alertDuration.TotalMinutes}分钟"; return GuardResult.Warning; // 抛出信号,发邮件! } else { // 还没到时间 return GuardResult.Normal; } } else { // 3. 内存正常 // 逻辑:如果之前在计时(比如超了1分钟),现在降下来了,则计时器清零。 // 但如果已经报过警 (_isAlertLatched=true),则保持锁定,不自动复位。 // 除非用户手动点 Reset。 if (_firstOverLimitTime != null) { _firstOverLimitTime = null; // 波动防抖:由于降下来了,重置观察计时 } return GuardResult.Normal; } } catch { return GuardResult.Normal; } } /// /// 用户点击“已处置”时调用 /// public void Reset() { _isAlertLatched = false; _firstOverLimitTime = null; } private string FormatSize(long bytes) => $"{bytes / 1024 / 1024}MB"; } }