簡體   English   中英

使用自引用字典序列化標記為AsReferenceDefault的類時,protobuf-net中的Stackoverflow異常

[英]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類的實例,則只有當他的設備的PathToPathFrom 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM