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
相关产品推荐
相关产品推荐

