【问题标题】:Adding .net COM wrapper to cache by value instead of by ref将 .net COM 包装器添加到按值缓存而不是按引用缓存
【发布时间】:2011-01-02 10:38:45
【问题描述】:

在我的代码中,我通过 COM 接口实例化了旧版 Delphi 对象。 此类需要多次实例化,因此为了降低实例化的开销,我将其缓存在所有调用的 70% 具有公共结果对象的位置。

但是,当我在缓存后更改对象时,更改也会保留在缓存中。这让我觉得 COM 包装器实例是通过 ref 而不是按值传递的。
如何确保缓存中的对象是按值传递而不是按引用传递?

【问题讨论】:

  • 当您写下您更改“对象”时,您指的是 Delphi COM 对象还是您的 RCW .NET 端包装代理?当然,COM 对象总是通过引用传递,因此如果您缓存它,则应该共享对其状态的更改......也许一个简短的例子可以澄清您的问题。
  • 之后的更改是在代理类上完成的。我将尝试添加一个示例,但与往常一样,带有遗留对象的代码非常混乱。

标签: c# com pass-by-reference pass-by-value


【解决方案1】:

我认为,如果有可能,您需要显式克隆对象的副本,然后缓存该副本。例如,参见MemberwiseClone 方法,以及Cloning objects in C# 的答案中提到的其他方法。

【讨论】:

  • 我什至不确定 MemberwiseClone 是否可以在 COM 对象上按预期工作。它充其量可以克隆公共属性,但不能克隆任何通过方法改变的内部状态。
  • 是的,我认为它充其量只会克隆包装器,而不是克隆正在包装的实际 COM 对象。
  • 该对象有一个“exportToXml”和“importFromXml”,虽然它们相对昂贵,但我想我没有其他选择了。
【解决方案2】:

首先,这需要吗?

我不提倡“衡量解决所有性能问题”,但在你的情况下,你应该这样做。

实例化 COM 对象(在第一次调用惩罚之后)的开销非常低 - 请记住,它被设计为在 15 年前的计算机上提供许多小对象。我假设 .NET 开销并不多 - 所以问题是对象自己的初始化。

您可以通过在一个紧密循环中实例化 1000 个对象来轻松检查这一点(丢弃第一个调用,它可能非常昂贵并且会破坏平均值)

COM 对象本质上是通过引用
COM 对象没有“按值传递”,因为它们的基本接口是指向实例的引用计数指针,并且 COM 不公开通用的“克隆”方法。

可能的解决方案:写时复制
如果仅当实例化真的开销很大,并且大多数调用可以通过默认实例完成,则可以实现写时复制方案。

您需要创建一个包装类,其中包含对默认实例的引用以及对初始化为 0 的私有实例的引用。

只要私有实例为null,所有getter函数都转发到默认实例,否则转发到私有实例。

每个 setter/mutator 调用都转发到私有实例,当它不存在时创建它。

这会将私有实例的创建延迟到第一次变异调用。但是,您必须包装此组件的所有感兴趣的接口。

【讨论】:

  • 感谢您关于“过早优化是魔鬼”的提醒。我知道测量的原理。在我的情况下,昂贵的不是实例化,而是初始化(获取一堆 DB 值,将它们插入对象并触发对象内部的逻辑)。每个请求都会执行多次初始化,每个站点每分钟会发出数千个请求。正如性能分析 (Ants) 所指出的,这是当前(即使有缓存)整个应用程序的瓶颈。我会考虑实施您的最终建议
  • 此外,应用程序的遗留逻辑不允许我审查应用程序的设计以规避除了缓存之外的重复初始化。
  • Boris,我认为这是投资的正当理由——即使只是为了消除访问数据库的“故障点”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-18
  • 2021-12-10
  • 1970-01-01
  • 1970-01-01
  • 2019-03-23
  • 1970-01-01
  • 2014-05-18
相关资源
最近更新 更多