Files
Ayay/SHH.MjpegPlayer/Server/MjpegSessions.cs

116 lines
3.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}