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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 19:18:24