.net 从终结器调用 RCW 是否安全?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1573977/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is it safe to call an RCW from a finalizer?
提问by Andrew Arnott
I have a managed object that calls a COM server to allocate some memory. The managed object must call the COM server again to free that memory before the managed object goes away to avoid a memory leak. This object implements IDisposableto help ensure that the correct memory-releasing COM call is made.
我有一个托管对象,它调用 COM 服务器来分配一些内存。托管对象必须再次调用 COM 服务器以在托管对象消失之前释放该内存,以避免内存泄漏。此对象实现IDisposable以帮助确保进行正确的内存释放 COM 调用。
In the event that the Disposemethod is notcalled, I would like the object's finalizer to free the memory. The trouble is, the rules of finalization is that you must not access any reference because you don't know what other objects have already been GC'd and/or finalized before you. This leaves the only touchable object state to be fields (handles being the most common).
如果未调用该Dispose方法,我希望对象的终结器释放内存。问题是,终结的规则是你不能访问任何引用,因为你不知道在你之前还有哪些其他对象已经被 GC 和/或终结。这使得唯一可触摸的对象状态是字段(最常见的是句柄)。
But calling a COM server involves going through an runtime callable wrapper (RCW) in order to free the memory that I have a cookie to stored in a field. Is that RCW safe to call from a finalizer (is it guaranteed to have not been GC'd or finalized at this point)?
但是调用 COM 服务器涉及通过运行时可调用包装器 (RCW) 以释放我有 cookie 存储在字段中的内存。 从终结器调用该 RCW 是否安全(是否保证此时尚未被 GC 或终结)?
For those of you not familiar with finalization, although the finalizer thread runs in the background of a managed appdomain while its running, at for those cases touching references would theoretically be OK, finalization also happens at appdomain shutdown, and in any order-- not just in reference relationship order. This limits what you can assume is safe to touch from your finalizer. Any reference to a managed object might be "bad" (collected memory) even though the reference is non-null.
对于那些不熟悉终结的人,虽然终结器线程在运行时在托管应用程序域的后台运行,但对于这些情况,理论上触摸引用是可以的,终结也发生在应用程序域关闭时,并且以任何顺序- 不是只是在参考关系顺序。这限制了您可以从终结器中安全触摸的内容。对托管对象的任何引用都可能是“坏的”(收集的内存),即使该引用是非空的。
Update: I just tried it and got this:
更新:我刚刚尝试过并得到了这个:
An unhandled exception of type 'System.Runtime.InteropServices.InvalidComObjectException' occurred in myassembly.dll
Additional information: COM object that has been separated from its underlying RCW cannot be used.
myassembly.dll 中发生类型为“System.Runtime.InteropServices.InvalidComObjectException”的未处理异常
附加信息:无法使用已与其底层 RCW 分离的 COM 对象。
采纳答案by Andrew Arnott
I found out from the CLR team themselves that indeed it is not safe -- unlessyou allocate a GCHandle on the RCW while it's still safe to do so (when you first acquire the RCW). This ensures that the GC and finalizer haven't totaled the RCW before the managed object that needs to invoke it is finalized.
我从 CLR 团队自己发现它确实不安全——除非你在 RCW 上分配一个 GCHandle,而这样做仍然是安全的(当你第一次获得 RCW 时)。这确保了 GC 和终结器在需要调用它的托管对象完成之前没有总计 RCW。
class MyManagedObject : IDisposable
{
private ISomeObject comServer;
private GCHandle rcwHandle;
private IServiceProvider serviceProvider;
private uint cookie;
public MyManagedObject(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
this.comServer = this. serviceProvider.GetService(/*some service*/) as ISomeObject;
this.rcwHandle = GCHandle.Alloc(this.comServer, GCHandleType.Normal);
this.cookie = comServer.GetCookie();
}
~MyManagedObject()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// dispose owned managed objects here.
}
if (this.rcwHandle.IsAllocated)
{
// calling this RCW is safe because we have a GC handle to it.
this.comServer.ReleaseCookie(this.cookie);
// Now release the GC handle on the RCW so it can be freed as well
this.rcwHandle.Free();
}
}
}
It turns out in my particularcase, my app is hosting the CLR itself. Therefore, it's calling mscoree!CoEEShutdownCOM before the finalizer thread gets to run, which kills the RCW and results in the InvalidComObjectExceptionerror I was seeing.
事实证明,在我的特殊情况下,我的应用程序托管 CLR 本身。因此,它在终结器线程开始运行之前调用 mscoree!CoEEShutdownCOM,这会杀死 RCW 并导致InvalidComObjectException我看到的错误。
But in normal cases where the CLR is not hosting itself, I'm told this should work.
但在 CLR 不自行托管的正常情况下,我被告知这应该可行。
回答by JaredPar
No it is not safe to access a RCW from the finalizer thread. Once you reach the finalizer thread you have no guarantee that the RCW is still alive. It is possible for it to be ahead of your object in the finalizer queue and hence released by the time your destructor runs on the finalizer thread.
不,从终结器线程访问 RCW 是不安全的。一旦到达终结器线程,您就无法保证 RCW 还活着。它有可能在终结器队列中位于您的对象之前,因此在您的析构函数在终结器线程上运行时被释放。

