Protobuf.net异常 - 检查元数据时超时

时间:2011-09-10 15:07:04

标签: c# deadlock protobuf-net

我在有时在尝试使用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对两个相同类型的对象进行反序列化会发生什么呢?这个对象在以前没有见过,只是在同一时间?

4 个答案:

答案 0 :(得分:9)

旧问题,但如果有人碰巧遇到此错误,请检查您正在使用的DLL版本。在便携版本中显示此异常的可能性非常高。

https://github.com/mgravell/protobuf-net/pull/98https://github.com/mgravell/protobuf-net/pull/114的Portable版本有关的此问题有几个PR。

答案 1 :(得分:3)

让我使用

准备序列化程序

Serializer.PrepareSerializer<Type>();

并没有真正帮助我。

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

MessageSerialization.Serialize(new Type());

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

答案 2 :(得分:2)

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

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

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

但奇怪的是, 仍然 不应该可能死锁 - 它故意使用粗锁来避免锁定的顺序问题。再一次 - 我真的很想看到样品。

答案 3 :(得分:1)

我在服务器上遇到同样的错误。虽然我不确定导致错误的原因。几天前,它们相隔几个小时发生了两次,而我们的服务器处于我们所经历的最高负载之下。运行8台服务器,所有CPU的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);
    }
}