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

如何在ASP.NET Core SignalR序列化前锁定共享对象?

多线程场景下SignalR序列化共享集合的线程安全问题

当存在被多线程操作的static List<int>对象时,不加锁直接枚举是不安全的。而从SignalR Hub方法返回该列表或发送给客户端时,SignalR会内部自动序列化且不会提前加锁,这会引发线程安全风险。

在ASP.NET Core ApiController中,我们可以通过自定义序列化流程规避该问题:

static List<int> list;
object Get()
{
    string s;
    lock(list)
    {
         s = JsonConvert.Serialize(list);
    }
    return new ContentResult(){ Content = s, ContentType = "application/json"};
}

这种方法虽不完美,但可解决问题。

针对SignalR场景,以下是几种解决方案的分析:

方案1:传递给SignalR前克隆对象

  • 实现思路:在将共享对象返回给SignalR前,先创建对象副本,确保序列化操作针对独立副本,避免与其他线程的修改操作冲突。
  • 缺点:克隆存在性能开销,集合元素较多时尤为明显;若为复杂对象,还需实现深克隆,增加代码复杂度。

方案2:利用Newtonsoft JSON的序列化回调与错误处理

在包含共享集合的类中定义序列化生命周期回调,序列化开始时加锁,完成或出错时释放锁:

// 从Hub方法返回的共享对象
class SomeObject
{
    public List<int> list;

    [OnSerializing]
    internal void OnSerializingMethod(StreamingContext context)
    {
        Monitor.Enter(this);
    }
    [OnSerialized]
    internal void OnSerializedMethod(StreamingContext context)
    {
        Monitor.Leave(this);
    }
    [OnError]
    internal void OnError(StreamingContext context, ErrorContext errorContext)
    {
        Monitor.Leave(this);
    }
}
  • 注意事项:需确认OnSerializing与OnSerialized、OnSerializing与OnError是否严格成对调用。若序列化过程中出现未捕获异常导致回调未触发,可能引发死锁风险。建议结合try/finally逻辑确保锁释放,或使用lock语法糖替代直接调用Monitor方法。

方案3:SyncRoot是否适用?

SyncRoot是ICollection接口定义的用于同步集合访问的对象,通常用于手动控制集合的线程安全访问。但SignalR的序列化流程由框架内部完成,无法直接在序列化过程中利用SyncRoot加锁——除非拦截序列化流程并手动使用SyncRoot加锁,这本质上和ApiController中自定义序列化的思路一致,只是换了锁对象。因此SyncRoot本身无法直接解决SignalR自动序列化的线程安全问题,需结合自定义序列化或回调机制使用。


内容的提问来源于stack exchange,提问作者ywq

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 01:45:02