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";
}
}