增加 SmartFrame 强制收回逻辑

This commit is contained in:
2026-01-17 15:41:55 +08:00
parent e06c60968d
commit 2992306056
7 changed files with 205 additions and 48 deletions

View File

@@ -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>

View File

@@ -181,4 +181,34 @@ public class SmartFrame : IDisposable
}
#endregion
// 在 SmartFrame 类中添加此方法
/// <summary>
/// [哨兵专用] 强制重置帧状态
/// 用于自愈机制:当引用计数由于逻辑 Bug 永久无法归零时,由 FramePool 强行回收
/// </summary>
internal void ForceReset()
{
// 1. 强行将计数器和归还标记归零
Interlocked.Exchange(ref _refCount, 0);
Interlocked.Exchange(ref _isReturned, 1);
// 2. 清理衍生数据,防止内存堆积
ResetDerivatives();
// 3. 标记已被强制回收,便于日志追踪
IsForceRecycled = true;
}
// 记录帧被从池中取出的精确时间
public long BorrowedTick { get; private set; }
// 标记是否已被哨兵强制回收
internal bool IsForceRecycled { get; set; }
public void MarkBorrowed()
{
BorrowedTick = Environment.TickCount64;
IsForceRecycled = false;
}
}