You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 16:05:04