【问题标题】:Protobuf.net Exception - Timeout while inspecting metadataProtobuf.net 异常 - 检查元数据时超时
【发布时间】:2011-09-10 15:07:04
【问题描述】:

有时在尝试使用 protobuf.net 反序列化对象时收到以下异常。我很惊讶,因为我从来没有超过一个线程同时反序列化同一个对象,并且 protobuf.net 源似乎没有使用任何静态对象进行反序列化。例外确实提出了一个解决方案,但我不确定如何实施,所以欢迎举个例子。

Base Exception Type:
System.TimeoutException: Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection
at ProtoBuf.Meta.RuntimeTypeModel.TakeLock(Boolean& lockTaken)
at ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(Type type, Boolean demand, Boolean addWithContractOnly, Boolean addEvenIfAutoDisabled)
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)

Inner Exception Type:
System.TimeoutException: Timeout while inspecting metadata; this may indicate a deadlock. This can often be avoided by preparing necessary serializers during application initialization, rather than allowing multiple threads to perform the initial metadata inspection
at ProtoBuf.Meta.RuntimeTypeModel.TakeLock(Boolean& lockTaken)
at ProtoBuf.Meta.RuntimeTypeModel.FindOrAddAuto(Type type, Boolean demand, Boolean addWithContractOnly, Boolean addEvenIfAutoDisabled)
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)

Stack Trace: 
at ProtoBuf.Meta.RuntimeTypeModel.GetKey(Type type, Boolean demand, Boolean getBaseKey)
at ProtoBuf.Meta.TypeModel.GetKey(Type& type)
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type)

问候, 马克

编辑添加:我这样定义我的可序列化对象:

[ProtoContract]
public class Job
{
    [ProtoMember(1)]
    public long JobId { get; private set; } 
}

我很难在每个可序列化对象上轻松调用 PrepareSerialiser,因为我在不同的命名空间中有很多。但是考虑一下,如果要求 protobuf 在完全相同的时间反序列化两个相同类型的对象,这是它以前从未见过的类型,会发生什么?

【问题讨论】:

  • 嘿,马克。如果我能可靠地重现该问题,我肯定会提供更多信息。不幸的是,这种情况发生在如此小比例 (0.01%) 的情况下,事实证明非常很难确定。我序列化的所有对象都相对较小,可能只有 10 个值类型和几个列表。我会找出可以在哪里添加 prepareSerializer 调用,如果问题仍然存在,请告诉您。
  • 我很感兴趣...我将尝试浏览代码以查找可能导致此问题的任何内容(感谢包含完整的堆栈跟踪,顺便说一句 - 这很有帮助)。这……很奇怪。
  • 值得补充的是,虽然我只有一个线程反序列化一个唯一的字节 [],但我可能有多达 100 个线程同时反序列化 100 个唯一的字节 []。可能是与 CPU 负载有关的问题,即开始反序列化和结束之间的时间太长?

标签: c# deadlock protobuf-net


【解决方案1】:

老问题,但如果有人碰巧得到这个错误,请检查你正在使用的 DLL 版本。此异常出现在便携版本中的可能性非常高。

便携版有几个与此问题相关的 PR,它们是 https://github.com/mgravell/protobuf-net/pull/98https://github.com/mgravell/protobuf-net/pull/114

【讨论】:

  • 我在线程环境中使用 2.0.0.640 PCL 库的经历非常一致。在更高级别的项目中添加对显式版本的引用可以通过将 PCL 版本替换为特定于该更高级别程序集(SL、.Net 40 等)的版本来解决此问题,从而立即解决问题。
  • 这个答案和评论为我节省了很多时间。很棒的见解,朋友们。
  • PCL 版本 2.0.0.668 对我来说始终出现此错误。我还尝试增加上面建议的超时@MarcGravell,但这并不能解决问题。除了切换到 .DLL 的各个 CLR 特定版本之外,还有其他线索吗?
  • 在 2016 年,这仍然是个问题。我在一次部署中不小心用 PCL 版本替换了正常的 2.0.0.668,并且立即出现了许多超时错误。删除 PCL 有助于解决问题。
【解决方案2】:

RuntimeTypeModel.Default(默认模型)是静态的,是静态 Serializer 类的基础(即没有任何静态状态的注释)。尽管由于偏执而添加了此检查,但我从未看到此错误出现。我非常希望看到一个可以重现这一点的例子。你确定你不是线程?如果不是线程,我只能想知道:类型模型真的真的大吗?

确实,即使有许多线程在启动时积极地攻击它(即在 stackoverflow 上),它也表现良好。正如错误消息提示的那样,您可以尝试在应用启动期间调用 Serializer.PrepareSerializer,这将预先初始化所有内容,避免任何线程问题。

但是,嘿!至少没有死锁!

然而,这里的奇怪之处在于它仍然不应该可能死锁 - 它故意使用粗锁来避免锁定顺序的问题。再说一次 - 我真的很想看看样品。

【讨论】:

  • @MarcF - 作为一个随机的东西,你可以尝试将 RuntimeTypeModel.Default.MetadataTimeoutMilliseconds 设置为一个愚蠢的大东西,看看这是否是一个真正的死锁与比正常时间更长的东西?
  • 抱歉这个回复拖了这么久。我刚刚浏览了一些旧的 stackoverflow 帖子。我确实将 MetadataTimeoutMilliseconds 增加到 '150000' 并且我认为从那以后我没有看到错误。
  • 只是想补充一下,我也遇到了这个错误。在数千次测试中只有 1 次,但我确实有多个线程在做这项工作。 (4 个大气压,但会随着更多事物的出现而增长)
  • 这只是在我对所有内核执行 CPU 密集型工作时发生的。我的整个机器很慢。许多线程试图获取锁,其中一个线程处于某种“编译”方法中。
  • @AgileJon 如果你想完全消除它,你可以使用单独的预编译器 exe 生成一个序列化 dll,然后永远不需要做任何元数据检查
【解决方案3】:

为我准备序列化器使用

Serializer.PrepareSerializer<Type>();

并没有真正帮助我。

对我来说,解决方案(又名解决方法)是在启动时序列化类型,这会在应用启动时造成问题:

MessageSerialization.Serialize(new Type());

其中MessageSerialization.Serialize是使用protobuf Serializer.Serialize(stream, o)的序列化方法

【讨论】:

    【解决方案4】:

    我的服务器上也出现了同样的错误。虽然我不确定是什么导致了错误。几天前,当我们的服务器处于我们经历过的最高负载时,它每隔几个小时就发生了两次。运行 8 台服务器,所有服务器的 CPU 在几秒钟内从 70% 上升到 100%,但时间略有不同。例如,每个服务器可能在第一个服务器后 1-5 分钟就开始了这个峰值。

    以前从未见过这种情况,我已经在生产中使用了几个月的代码。仍然无法重现它,我不知道是否抛出错误是因为服务器处于 100% CPU,或者这是否是导致服务器峰值的原因。停止与服务器的所有连接并让 cpu 回落到 0 解决了这个问题。不需要重新启动 iis。

    当 IIS 启动时,我为每种类型运行一次以下代码。

    var type = this.GetType();
    RuntimeTypeModel.Default.Add(type, true);
    Int32 i = 1;
    foreach(PropertyInfo info in type.GetProperties())
    {
        if(info.CanWrite)
        {
            RuntimeTypeModel.Default[type].AddField(i++, info.Name);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-02
      • 2019-07-22
      • 2014-10-28
      • 2022-10-13
      • 2017-01-01
      • 2020-09-15
      相关资源
      最近更新 更多