【问题标题】:How to serialize an object that has a constantly updated collection如何序列化具有不断更新集合的对象
【发布时间】:2016-06-28 17:36:44
【问题描述】:

我有一个缓存服务,它保存多个 Price 对象,这些对象会随着新价格增量的到来而更新,有时每秒会更新多次。 每个对象在分配给 ID 的集合中保存它的各种价格。如果有人订阅特定价格,我需要在每次新价格到达时将最新价格对象序列化为 JSON,以便通过 RMQ 发送。我遇到的问题是,在某些情况下,我在序列化时收到以下错误消息,因为新价格已经到达并在之前的序列化过程中更新了对象上的集合。

“集合已修改;枚举操作可能无法执行。”

我尝试了各种序列化对象的方法(它需要尽可能快),但我仍然遇到同样的问题。

解决这个问题的最好和最有效的方法是什么,这样即使对象发生变化我也可以序列化。

简化的对象是:

 //This is the collection on an object that holds the prices which are being updated
 public ConcurrentDictionary<Id, Prices> Asset{ get; set; } 

 //Class that holds the ever updating prices
 [Serializable] 
  public class Prices 
  {

      public Prices()
      {
          Prices1 = new List<PriceVolume>();
          Prices2 = new List<PriceVolume>();
       }
   }

提前致谢!

【问题讨论】:

  • 我能想到的最简单的方法是在序列化之前创建一个深拷贝。

标签: c# serialization collections


【解决方案1】:

您应该创建它的深层副本并序列化副本,而不是序列化您从并发字典中提取的实际对象。不幸的是,您仍然必须将代码放入互斥锁中以获得副本。 ConcurrentDic 仅保护您在检索项目时不会更改或删除它,它不会在您检索到对它的引用后保护对象免受操作。

【讨论】:

  • 如果你必须锁定并发字典,那么首先创建一个深拷贝有什么意义?
  • dic 只锁定对象以进行检索,它不会阻止它在序列化时被更改。您将不得不锁定整个拉取和序列化的部分,或者将锁保持在拉取和复制的部分更小,无论哪种方式,字典都不会在这里为您提供任何安全性。
  • 我认为可能有一种巧妙的方法可以避免深层复制和序列化步骤,但是正如您所说,我越研究它似乎是不可避免的。正如您所说,我仍然必须处理在此过程中更新的对象。非常感谢
【解决方案2】:

在序列化元素时,您可能会从 locking 元素中受益。

当您在锁内时,锁定可防止其他线程修改元素。

这将使尝试更改价格的操作等到您完成序列化后才能修改它们。

[Serializable]
public class Prices
{
    public string Serialize()
    {
        lock (this)
        {
            // logic for serilization here
        }
    }
}

【讨论】:

  • 类接口也必须改变,因为 ConcurrentDictionary 目前是一个公共字段。
  • 该锁不会阻止其他线程更改内容。它只阻止知道并同意遵循锁定约定的代码进行更改。 lock 是一种协作互斥设备。如果你想在这里使用lock,那么所有访问字典的代码也必须使用lock。一切都很好,除了这会消除使用ConcurrentDictionary 的任何好处。
【解决方案3】:

只是一个想法,但是如何研究序列化回调(有些人将其称为序列化挂钩)并实现 ISerializable 接口。您似乎需要对对象的序列化进行更细粒度的控制。看看这个链接:

Custom Serialization

看下面

  • OnDeserializingAttribute(反序列化之前)
  • OnDeserializedAttribute(反序列化后)
  • OnSerializingAttribute(序列化之前)
  • OnSerializedAttribute(序列化后)

也可以看看这个链接:

Version Tolerant Serialization

您可以考虑对象是否需要在某些字段上使用 OptionalFieldAttributeNonSerializedAttribute 来控制它们是否需要序列化或可选序列化。请注意 NonSerializedAttribute 的使用。看看文章中提到的最佳实践(此处转载以供参考):

为确保正确的版本控制行为,在不同版本之间修改类型时请遵循以下规则:

  • 永远不要删除序列化字段

  • 如果以前版本中未将属性应用于字段,则切勿将 NonSerializedAttribute 属性应用于字段

  • 切勿更改序列化字段的名称或类型

  • 添加新的序列化字段时,应用 OptionalFieldAttribute 属性
  • 从字段中删除 NonSerializedAttribute 属性(在以前的版本中不可序列化)时,应用 OptionalFieldAttribute 属性

  • 对于所有可选字段,使用序列化回调设置有意义的默认值,除非可以接受 0 或 null 作为默认值

为确保类型与未来的序列化引擎兼容,请遵循以下准则:

  • 始终正确设置 OptionalFieldAttribute 属性的 VersionAdded 属性
  • 避免分支版本控制

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 2020-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多