3

该类SafeHandle允许通过引用计数和 P/Invoke 编组器的合作安全访问 .NET 下的本机资源,以避免过早处理可能导致应用程序崩溃的句柄。

在某些情况下,通知垃圾收集器某些本机资源正在使用大量内存是有利的,例如,当所讨论的“句柄”是一个封装的本机库拥有的大缓冲区时。GC.AddMemoryPressure可用于此目的;但是,GC.RemoveMemoryPressure必须在“句柄”的生命周期结束时调用。

这两种方法似乎是不兼容的:SafeHandleis aCriticalFinalizerObject和它的ReleaseHandle方法在受约束的执行区域 (CER) 中运行。与GC.RemoveMemoryPressurenoReliabilityContractAttribute一样,不能从 CER 调用它(我猜在关键的完成时间,与内存压力有关的 GC 的某些部分可能不可用)。

我想到了两种方法,都非常不雅:

  1. 可以将 SafeHandle 包装到另一个对象中,该对象具有添加和删除内存压力的非关键终结器(和 Dispose 方法)。这有两个不足:首先,有人可以在不处理包装器的情况下处理 SafeHandle(它必须暴露才能传递给本地方法),因此内存压力被高估了。其次,当 SafeHandle 还活着时,包装器可能会变得未被引用,因此内存压力被低估了(可能更严重)。

  2. SafeHandle 有一个管理内存压力的非关键可终结类型的字段。在这种情况下,上面的大多数问题都消失了,但是,句柄无法在其 Dispose 或 ReleaseHandle 方法中释放内存压力对象,因为这会导致 CER 中的 GC.RemoveMemoryPressure 调用。可以提供另一种方法(例如 DisposeNoncriticial),但是当句柄被强制转换为 IDisposable 或在 using 块中使用时,这会导致“切片”问题。

是否有创建具有相关内存压力的 SafeHandle 的通用模式?

4

1 回答 1

3

这是一个很好的模式来包裹一个句柄而不是在正常情况下暴露它。你可以像FileStream. 据了解,该句柄为该句柄所有,FileStream关闭该句柄属违法行为。这不是强制执行的,但它是合同。

第二个问题是您希望将 的生命周期SafeHandle与其包装器联系起来。该ConditionalWeakTable课程允许您这样做。设计看起来像这样:

class Wrapper {
 SafeHandle handle;
 CWT cwt = new CWT() { { handle, this } };

 ~Wrapper() { RemoveMemoryPressure(); }
}

如果句柄处于活动状态,则 CWT 使包装器保持活动状态。CWT 用于将任意状态与不可扩展的对象相关联,并且不会产生任何 GC 效果。

我不确定所有这些是否值得,但这将是(主要)满足您的要求的解决方案。

另外,我不确定内存压力在多大程度上有实际用途。

于 2016-08-03T21:34:58.360 回答