SignalR:用户断开连接或关闭浏览器时取消订阅定时器事件
SignalR 基于在线用户管理定时器启停方案
当前代码的核心问题是未将定时器生命周期与在线用户数关联,导致可能出现重复定时器或不必要的资源消耗。以下是优化后的实现方案,确保仅在有用户在线时运行定时器,最后一个用户断开时自动停止。
修改后的Hub连接事件处理
public override Task OnConnected() { var wasEmpty = UserHandler.ConnectedIds.Count == 0; UserHandler.ConnectedIds.Add(Context.ConnectionId); // 第一个用户连接时启动网格刷新定时器 if (wasEmpty) { StartServiceTimer(RefreshGridForAllUsers); } return base.OnConnected(); } public override Task OnDisconnected(bool stopCalled) { UserHandler.ConnectedIds.Remove(Context.ConnectionId); // 最后一个用户断开时停止并清理定时器 if (UserHandler.ConnectedIds.Count == 0) { ClearServiceTimers(); } return base.OnDisconnected(stopCalled); } // 向所有在线用户推送网格更新的逻辑 private void RefreshGridForAllUsers() { var latestData = FetchLatestGridData(); Clients.All.SendAsync("UpdateGrid", latestData); } // 模拟获取最新网格数据的方法 private object FetchLatestGridData() { // 替换为实际的数据查询逻辑 return new { /* 数据字段 */ }; }
优化后的定时器管理代码
// 存储定时器实例及对应的事件处理器,用于后续移除绑定避免内存泄漏 private Dictionary<string, (Timer Timer, ElapsedEventHandler Handler)> Timers { get; } = new(); private const double timerIntervalDefault = 5000; // 默认5秒刷新间隔 protected void StartServiceTimer(Action timerElapsedHandler, double? timerElapsedInterval = null) { var timersDict = new Dictionary<double, Action> { { timerElapsedInterval ?? timerIntervalDefault, timerElapsedHandler } }; StartServiceTimers(timersDict); } protected virtual void StartServiceTimers(Dictionary<double, Action> timersElapsedHandlers) { foreach (var entry in timersElapsedHandlers) { var methodName = entry.Value.Method.Name; if (Timers.ContainsKey(methodName)) continue; var timer = new Timer { Interval = entry.Key }; ElapsedEventHandler elapsedHandler = (sender, args) => entry.Value(); timer.Elapsed += elapsedHandler; timer.Start(); // 保存定时器和事件处理器 Timers.Add(methodName, (timer, elapsedHandler)); // 立即执行一次刷新,避免用户等待 elapsedHandler.Invoke(this, null); } } protected void StopServiceTimers() { foreach (var timerEntry in Timers) { var (timer, handler) = timerEntry.Value; timer.Elapsed -= handler; // 移除事件绑定 timer.Stop(); timer.Dispose(); // 释放资源 } } protected void ClearServiceTimers() { StopServiceTimers(); Timers.Clear(); }
关键说明
- 单实例定时器:同一个刷新逻辑仅对应一个定时器实例,无论多少用户在线,避免重复执行刷新操作
- 用户数联动:定时器仅在有用户在线时运行,最后一个用户断开时自动清理资源
- 内存泄漏防护:停止定时器时移除事件绑定并释放定时器资源,防止内存泄漏
- 即时反馈:用户连接后立即执行一次刷新,无需等待定时器间隔
内容的提问来源于stack exchange,提问作者Arun Singh
相关产品推荐
相关产品推荐

