增加 SmartFrame 强制收回逻辑
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using OpenCvSharp;
|
||||
using Ayay.SerilogLogs;
|
||||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
|
||||
namespace SHH.CameraSdk;
|
||||
|
||||
@@ -12,6 +14,8 @@ namespace SHH.CameraSdk;
|
||||
/// </summary>
|
||||
public class FramePool : IDisposable
|
||||
{
|
||||
private ILogger _sdkLog => Log.ForContext("SourceContext", LogModules.HikVisionSdk);
|
||||
|
||||
#region --- 私有资源与配置 (Private Resources & Configurations) ---
|
||||
|
||||
/// <summary> 可用帧队列(线程安全):存储待借出的空闲智能帧 </summary>
|
||||
@@ -79,12 +83,13 @@ public class FramePool : IDisposable
|
||||
/// 从池借出一个智能帧(O(1) 时间复杂度)
|
||||
/// </summary>
|
||||
/// <returns>可用智能帧 / 池空且达上限时返回 null(触发背压丢帧)</returns>
|
||||
public SmartFrame Get()
|
||||
public SmartFrame? Get()
|
||||
{
|
||||
// 1. 优先从可用队列取帧,无锁快速路径
|
||||
if (_availableFrames.TryDequeue(out var frame))
|
||||
{
|
||||
frame.Activate();
|
||||
frame.MarkBorrowed(); // 记录起始时间
|
||||
return frame;
|
||||
}
|
||||
|
||||
@@ -104,11 +109,57 @@ public class FramePool : IDisposable
|
||||
return Get();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 3. [自愈触发] 如果走到这里,说明池子满了且所有帧都在外借中。
|
||||
// 可能存在“僵尸帧”死锁。执行强制回收哨兵。
|
||||
// ============================================================
|
||||
if (ForceRecycleZombies())
|
||||
{
|
||||
// 如果哨兵成功救回了至少一帧,递归重试就能拿到帧
|
||||
return Get();
|
||||
}
|
||||
|
||||
// 3. 背压策略:池空且达上限,返回 null 强制丢帧,保证生产端不阻塞
|
||||
// 适用场景:消费端处理过慢导致帧堆积,丢帧保实时性
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 哨兵巡检:强制回收占用超过 5 秒不还的僵尸帧
|
||||
/// </summary>
|
||||
private bool ForceRecycleZombies()
|
||||
{
|
||||
bool anyRescued = false;
|
||||
long now = Environment.TickCount64;
|
||||
|
||||
// Optimized: [原因] 使用 lock 或 ToArray() 防止在哨兵巡检期间,
|
||||
// 其他线程触发 CreateNewFrame 导致 List 集合修改异常。
|
||||
SmartFrame[] snapshot;
|
||||
lock (_lock) { snapshot = _allAllocatedFrames.ToArray(); }
|
||||
|
||||
// 遍历所有已分配的帧,找出超时的僵尸
|
||||
foreach (var frame in snapshot)
|
||||
{
|
||||
// 条件:当前不在池中(_refCount > 0)且借出时间超过 2000ms
|
||||
// 注意:这里需要给 SmartFrame 暴露一个只读的 RefCount 属性,或者直接判断 BorrowedTick
|
||||
if ((now - frame.BorrowedTick) > 2000)
|
||||
{
|
||||
// 强行重置该帧的所有权
|
||||
frame.ForceReset();
|
||||
|
||||
// 重新塞回队列
|
||||
_availableFrames.Enqueue(frame);
|
||||
|
||||
anyRescued = true;
|
||||
|
||||
// 记录一条警告日志,告诉你哪路视频出问题了
|
||||
// 可以在 AddAuditLog 里看到
|
||||
_sdkLog.Warning("[Sdk] SmartFrame(借出超2秒) 被强制回收.");
|
||||
}
|
||||
}
|
||||
return anyRescued;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [系统内部调用] 将帧归还至池(由 SmartFrame.Dispose 自动触发)
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user