[英]Stackoverflow exception in protobuf-net when serialising Class marked as AsReferenceDefault with self-referencing Dictionaries
我使用protobuf-net將方向圖序列化為文件。
我的(簡化的)類如下:
[ProtoContract]
public class Network
{
// All of the devices on the network
[ProtoMember(1)]
public readonly Dictionary<int, Device> Vertex;
// The list of connections
[ProtoMember(2)]
public readonly Dictionary<int, List<Device>> Nodes;
}
[ProtoContract(AsReferenceDefault = true)]
public class Device
{
[ProtoMember(1)]
public readonly int Id;
// All the devices with a direct path to this node
[ProtoMember(2)]
public readonly Dictionary<int, Device> PathTo;
// All the devices directly reachable from this node
[ProtoMember(3)]
public readonly Dictionary<int, Device> PathFrom;
// the nodes that this is connected to
[ProtoMember(4)]
public readonly int[] Nodes = new int[2];
}
如果我嘗試使用protobuf-net序列化Network
類的實例,則只有當他的設備的PathTo
和PathFrom
Dictionaries為空時, PathFrom
才起作用。
一旦我開始為每個Device
填充這些詞典(注意圖的方向),嘗試使用protobuf-net進行序列化就會引起stackoverflow。
有誰知道為什么它會堆棧溢出?
我已經閱讀了以下問題: 使用字典的Protobuf網絡對象引用反序列化:在反序列化過程中 , 引用跟蹤的對象更改了引用 ,並且根據Mark對他的回答的編輯,他通過添加AsReferenceDefault屬性來固定此圖引用,在Device
類上。
對我來說,遍歷列表時好像將字典的所有元素都視為唯一的個體。
考慮到網絡中約有300萬台設備,這很快會導致堆棧溢出。
調試窗口堆棧的屏幕截圖:
( 鏈接到完整圖像 )
作為優化的一部分,我調整了邊緣的存儲方式,因此最終還修改了數據結構,以便可以從對象引用中刪除循環。
我創建了Edge
類來跟蹤對象之間的所有邊緣以及邊緣的方向,並向Graph
添加了OnDeserialized
方法,以便它將貫穿所有邊緣並將所有引用重新添加到每個鏈接中類。
額外的好處是,此數據結構可以快速保存/加載(對於〜3,000,000個頂點,〜5,000,000個邊,其加載大約需要18s,而對於舊結構則需要30s),並且遍歷更快(對於3s完整遍歷並修改鏈接,而不是舊結構的〜50s)。
[ProtoContract]
public class Graph
{
// All of the devices on the graph
[ProtoMember(1)]
public readonly Dictionary<int, Vertex> Vertex;
// All of the links in the graph
[ProtoMember(2)]
public readonly List<Edge> Links;
[OnDeserialized]
public void OnDeserialized() {
// Add references to all of the links
foreach (Edge l in Links) {
l.Vertices[0] = this.Vertex[l.VertexIds[0]];
l.Vertices[1] = this.Vertex[l.VertexIds[1]];
}
}
}
// A vertex of the graph
[ProtoContract]
public class Vertex
{
[ProtoMember(1)]
public readonly int Id;
/*
we index the edges by the ID of the other device.
It makes it easy to find specific edges and makes it trivial to only track 1 copy of an edge
*/
[ProtoMember(2)]
public readonly Dictionary<int, Edge> Edges;
}
// An edge
[ProtoContract(AsReferenceDefault = true)]
public class Edge
{
public readonly Vertex[] Vertices;
[ProtoMember(1)]
public readonly int[] VertexIds;
[ProtoMember(2)]
public readonly Direction Direction;
public Edge() {
this.Vertices = new Vertex[2];
this.Direction = Direction.None;
/*
important to note that we don't set the VertexIds array in this constructor.
protobuf-net will create it for us, and if we create it here, we'll end up with a 4 length array.
*/
}
}
/*
denotes to which index in a edge's devices array the direction the edge goes.
[Flags] so we can do checks easier:
Edge.Direction == Direction.Both implies Edge.Direction & Direction.ZeroToOne != 0
*/
[Flags]
public enum Direction {
None = 0,
ZeroToOne = 1,
OneToZero = 2,
Both = 3
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.