.NET 8 SignalR 出现无法访问已释放对象问题求解答
在我的SignalR Hub中,偶尔会出现「无法访问已释放对象」的问题,但无法主动复现该场景。我的Hub代码如下:
namespace auction_hub_base { public class Auction_hub_base : Hub<IAuction_hub_base> { private static IDisposable auctionSubscription; public Auction_hub_base(IConfiguration config, IAMSLiveEventWrapper ams) : base() { configuration = config; if (auctionSubscription != null) { auctionSubscription.Dispose(); } auctionSubscription = SingletonReactiveLayer.Auction.Subscribe(state => { if (state == null) { Auction(null); } else { // Updated when the method 'Auction.OnNext' is called in singleton AuctionResponse res = new AuctionResponse(state.Id, state.EntityId, state.CurrentBatch, state.Finished, state.MaxBidValue); Auction(res); } }); } public void RegisterConnectionId(int clientId) { SingletonReactiveLayer.RegisterConnectionId(Context.ConnectionId, clientId); } public Task Auction(AuctionResponse response) { return Clients.All.Auction(response); } } }
RegisterConnectionId的实现如下:
public static void RegisterConnectionId(string connId, int clientId) { lock (_auction_SyncRoot) { if (_auction != null) { if (!_auction.ClientIds.ContainsKey(clientId)) { _auction.ClientIds.Add(clientId, connId); } else { _auction.ClientIds[clientId] = connId; } Auction.OnNext(_auction); Batch.OnNext(_currentBatch); // using bid error subscription because here we only need to notify the person who is registering BidError.OnNext(_currentBatch?.WinnerBid); BatchMessage.OnNext(_batchMessage); } } }
该错误偶尔会在执行return Clients.All.Auction(response);时触发。已知通过注入IHubContext,改用return _hubContext.Clients.All.Auction(response);可解决问题,想请教该问题出现的原因是什么?
Hub实例的临时生命周期:SignalR的Hub实例是请求级别的临时对象——客户端的每一次连接建立、Hub方法调用都会创建新的Hub实例,请求处理完成后实例会被立即释放回收。你在Hub构造函数中订阅了单例的
SingletonReactiveLayer.Auction,这个订阅的回调会在后续异步触发,但此时对应的Hub实例大概率已经被释放。当回调调用Auction()方法并访问Clients.All时,本质是在访问已被释放的Hub实例的属性,因此抛出“无法访问已释放对象”的异常。静态订阅的冲突风险:你使用了
static修饰的auctionSubscription字段,每次新Hub实例创建时都会先释放旧订阅再重新绑定。这会导致两个问题:一是旧订阅的回调可能还在执行过程中就被强制释放,引发异常;二是后续触发的订阅回调绑定的是最新创建的Hub实例,而该实例同样会在某次请求结束后被释放,一旦回调在实例释放后执行,就会触发目标异常。IHubContext的核心优势:
IHubContext是SignalR提供的全局单例服务,它不依赖于任何临时Hub实例,而是直接与SignalR的连接管理系统交互。使用IHubContext.Clients发送消息时,不会绑定到某个已被释放的Hub实例,从根源上避免了访问无效对象的问题。
内容的提问来源于stack exchange,提问作者Gonçalo Bastos

