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

SignalR报错:无法访问已释放对象,定时推送消息问题求助

问题描述

需求是每隔一段时间向所有连接的SignalR客户端推送消息,但运行时抛出“cannot access a disposed object”错误。

原Hub代码

public class LockerHub : Hub
{
    private Timer signalRTimer;

    public LockerHub()
    {
        double interval = 5000; 
        signalRTimer = new Timer(interval);
        signalRTimer.Elapsed += OnSignalRTimerElapsed;
        signalRTimer.Start();
    }

    private void OnSignalRTimerElapsed(object sender, ElapsedEventArgs e)
    {
        GetAllStatusModel();
    }

    [HubMethodName("GetAllStatusModel")]
    public void GetAllStatusModel()
    {
        Clients.All.SendAsync("AllStatusModel", JsonConvert.SerializeObject(Locker.LockerBoxesState, Formatting.Indented));
    }
}

原测试客户端代码

static async void MyMethod()
{
    var hubUrl = "....";

    var connection = new HubConnectionBuilder()
        .WithUrl(hubUrl)
        .Build();

    connection.Closed += async (exception) =>
    {
        await Task.Delay(5000);
        await connection.StartAsync();
    };

    connection.On<string>("AllStatusModel", (message) =>
    {
        Console.WriteLine($"New Message: {message}");
    });

    try
    {
        await connection.StartAsync();

        Console.ReadKey();
    }
    catch (Exception ex)
    {          
    }
    finally
    {
    }
}
错误原因

SignalR的Hub对象是**瞬态(Transient)**的,每个客户端请求都会创建新的Hub实例,请求结束后Hub会被自动销毁释放。你在Hub构造函数中创建的Timer,其回调逻辑不会随Hub的释放而停止,当Timer触发回调时,尝试访问已经被释放的Clients对象,就会抛出已释放对象的异常。

解决方案

将定时推送逻辑抽离到独立的单例服务中,通过注入IHubContext<LockerHub>来实现消息推送,避免依赖具体的Hub实例。

1. 创建定时推送服务

public class LockerStatusPushService : IDisposable
{
    private readonly IHubContext<LockerHub> _hubContext;
    private Timer _pushTimer;

    public LockerStatusPushService(IHubContext<LockerHub> hubContext)
    {
        _hubContext = hubContext;
        // 初始化5秒间隔的定时器
        _pushTimer = new Timer(5000);
        _pushTimer.Elapsed += OnTimerElapsed;
        _pushTimer.Start();
    }

    private async void OnTimerElapsed(object sender, ElapsedEventArgs e)
    {
        var statusJson = JsonConvert.SerializeObject(Locker.LockerBoxesState, Formatting.Indented);
        await _hubContext.Clients.All.SendAsync("AllStatusModel", statusJson);
    }

    public void Dispose()
    {
        _pushTimer?.Dispose();
    }
}

2. 修改Hub类

Hub仅保留手动触发推送的方法(可选),移除内部Timer逻辑:

public class LockerHub : Hub
{
    [HubMethodName("GetAllStatusModel")]
    public async Task GetAllStatusModel()
    {
        var statusJson = JsonConvert.SerializeObject(Locker.LockerBoxesState, Formatting.Indented);
        await Clients.All.SendAsync("AllStatusModel", statusJson);
    }
}

3. 注册服务到依赖注入容器

在Program.cs(或Startup.cs)中注册单例服务:

builder.Services.AddSingleton<LockerStatusPushService>();
// 确保SignalR服务已注册
builder.Services.AddSignalR();

4. 客户端代码优化(可选)

将async void改为async Task,避免异步方法的潜在问题:

static async Task MyMethod()
{
    var hubUrl = "....";

    var connection = new HubConnectionBuilder()
        .WithUrl(hubUrl)
        .Build();

    connection.Closed += async (exception) =>
    {
        await Task.Delay(5000);
        await connection.StartAsync();
    };

    connection.On<string>("AllStatusModel", (message) =>
    {
        Console.WriteLine($"New Message: {message}");
    });

    try
    {
        await connection.StartAsync();
        Console.ReadKey();
    }
    catch (Exception ex)
    {
        Console.WriteLine($"连接错误: {ex.Message}");
    }
}

内容的提问来源于stack exchange,提问作者Özgür Saklanmaz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 18:15:11