SafeHandle
类允许通过P / Invoke marshaller的引用计数和协作安全访问.NET下的本机资源,以避免过早丢弃可能导致应用程序崩溃的句柄。
在某些情况下,通知垃圾收集器某些本机资源正在使用大量内存将是有利的,例如,当" handle"问题是包装的本机库拥有的大缓冲区。 GC.AddMemoryPressure
可用于此目的;但是,GC.RemoveMemoryPressure
必须在"句柄"的生命周期结束时调用。
这两种方法似乎不兼容:SafeHandle
是CriticalFinalizerObject
,其ReleaseHandle
方法在约束执行区(CER)中运行。由于GC.RemoveMemoryPressure
没有ReliabilityContractAttribute
,所以不能从CER调用它(我猜在关键的最终确定时间,GC的一些与内存压力有关的部分可能无法使用)。
我想到了两种方法,两者都非常不优雅:
SafeHandle可以包装到另一个具有非关键终结器(和Dispose方法)的对象中,该对象可以添加和消除内存压力。这有两个缺点:首先,有人可以处置SafeHandle(它必须暴露给传递给本机方法)而不需要处理包装器,因此内存压力被高估了。其次,当SafeHandle还活着时,包装器可能会被取消引用,因此内存压力被低估(可能更严重)。
SafeHandle有一个非关键可终结类型的字段来管理内存压力。在这种情况下,上面的大多数问题都消失了,但是,句柄不能在其Dispose或ReleaseHandle方法中处理内存压力对象,因为这会导致CER中的GC.RemoveMemoryPressure调用。可以提供另一种方法(例如DisposeNoncriticial),但这会导致切片"将句柄强制转换为IDisposable或在使用块中使用时出现问题。
是否存在创建具有相关内存压力的SafeHandle的常见模式?
答案 0 :(得分:2)
这是一个很好的模式来包装句柄,而不是在正常情况下暴露它。你可以像FileStream
那样做那部分。据了解,句柄归FileStream
所有,关闭它是违法的。这不是强制执行,但它是合同。
第二个问题是您希望将SafeHandle
的生命周期与其包装器联系起来。 ConditionalWeakTable
类允许您这样做。设计看起来像这样:
class Wrapper {
SafeHandle handle;
CWT cwt = new CWT() { { handle, this } };
~Wrapper() { RemoveMemoryPressure(); }
}
如果句柄处于活动状态,CWT会使包装器保持活动状态。 CWT用于将任意状态与不可扩展的对象相关联,并且没有任何GC效果。
我不确定这一切是否值得,但它(或大部分)满足您的要求将是一个解决方案。
另外,我不确定记忆压力在多大程度上具有实际用途。