116 lines
3.5 KiB
C#
116 lines
3.5 KiB
C#
using Core.WcfProtocol;
|
||
using System.Collections.Concurrent;
|
||
|
||
namespace SHH.MjpegPlayer;
|
||
|
||
/// <summary>
|
||
/// 服务器会话集合 (线程安全重构版)
|
||
/// </summary>
|
||
public class MjpegSessions
|
||
{
|
||
// 核心改变:使用字典建立索引,Key = DeviceId#TypeCode
|
||
// 这样可以将查找特定摄像头的复杂度从 O(N) 降低到 O(1)
|
||
private readonly ConcurrentDictionary<string, List<MjpegSession>> _sessionMap
|
||
= new ConcurrentDictionary<string, List<MjpegSession>>();
|
||
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
public MjpegSessions()
|
||
{
|
||
PrismMsg<UploadImageRequest>.Subscribe(ProcUploadImageRequest);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 优化后的图片分发逻辑 (解决 O(N) 和 线程安全问题)
|
||
/// </summary>
|
||
/// <param name="req"></param>
|
||
public void ProcUploadImageRequest(UploadImageRequest req)
|
||
{
|
||
try
|
||
{
|
||
string key = $"{req.Id}#{req.Type}";
|
||
|
||
// 1. 更新通道信息 (ImageChannels 现已是线程安全的)
|
||
var chn = MjpegStatics.ImageChannels.Do(req, key);
|
||
|
||
// 2. O(1) 快速查找关注该流的会话列表
|
||
if (_sessionMap.TryGetValue(key, out var targetSessions))
|
||
{
|
||
// 必须加锁,防止遍历时 List 被其他线程(如 AddSession/RemoveSession) 修改
|
||
lock (targetSessions)
|
||
{
|
||
// 倒序遍历,方便在需要时移除失效会话
|
||
for (var i = targetSessions.Count - 1; i >= 0; i--)
|
||
{
|
||
var session = targetSessions[i];
|
||
session.DoImageProc(req);
|
||
}
|
||
}
|
||
|
||
if (chn != null) chn.IsPlaying = targetSessions.Count > 0;
|
||
}
|
||
else
|
||
{
|
||
if (chn != null) chn.IsPlaying = false;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
//Logs.LogWarning<MjpegSessions>(ex.Message, ex.StackTrace);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 添加会话
|
||
/// </summary>
|
||
/// <param name="session"></param>
|
||
public void AddSession(MjpegSession session)
|
||
{
|
||
if (session?.Info?.Key == null) return;
|
||
|
||
// 使用 GetOrAdd 确保线程安全地获取或创建 List
|
||
var list = _sessionMap.GetOrAdd(session.Info.Key, _ => new List<MjpegSession>());
|
||
|
||
lock (list)
|
||
{
|
||
list.Add(session);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 移除会话
|
||
/// </summary>
|
||
/// <param name="session"></param>
|
||
public void RemoveSession(MjpegSession session)
|
||
{
|
||
if (session?.Info?.Key == null) return;
|
||
|
||
if (_sessionMap.TryGetValue(session.Info.Key, out var list))
|
||
{
|
||
lock (list)
|
||
{
|
||
list.Remove(session);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前所有会话信息的快照 (用于 HTTP API 统计与展示)
|
||
/// [新增] 此方法替代旧版直接访问 Sessions 列表,防止 HTTP 线程与 MJPEG 线程发生冲突
|
||
/// </summary>
|
||
public List<SessionInfo> GetAllSessionInfos()
|
||
{
|
||
var result = new List<SessionInfo>();
|
||
// 遍历字典,线程安全地收集所有 Info
|
||
foreach (var kvp in _sessionMap)
|
||
{
|
||
// 对内部 List 加锁,确保复制过程不被打断
|
||
lock (kvp.Value)
|
||
{
|
||
result.AddRange(kvp.Value.Select(s => s.Info));
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
} |