在标准的处理/完成模式中,例如C#中带有Dispose()的终结器,如果从终结器调用该方法,则Dispose(bool)不会接触托管对象,这被认为是不安全的,因为它们可能已经被垃圾收集器收集了。
IntPtr etc有什么特别之处,可确保它们安全?
作为某些背景,为了使清理代码保持在分配代码附近,我在分配事件后立即向事件添加清理操作,然后从dispose方法调用该事件:
class TestClass : IDisposable
{
private IntPtr buffer;
public void test()
{
buffer = Marshal.AllocHGlobal(1024);
OnFreeUnmanagedResource += (() => Marshal.FreeHGlobal(buffer));
}
private List<IDisposable> managedObjectsToBeDisposed = new List<IDisposable>();
private event Action OnFreeUnmanagedResource = delegate { };
private bool _isDisposed = false;
private void Dispose(bool itIsSafeToAlsoFreeManagedObjects)
{
if (_isDisposed) return;
OnFreeUnmanagedResource();
if (itIsSafeToAlsoFreeManagedObjects)
for (var i = managedObjectsToBeDisposed.Count - 1; i >= 0; i--)
{
var managedObjectToBeDisposed = managedObjectsToBeDisposed[i];
managedObjectToBeDisposed.Dispose();
managedObjectsToBeDisposed.Remove(managedObjectToBeDisposed);
}
_isDisposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~TestClass()
{
Dispose(false);
}
}
我不确定此代码,因为OnFreeUnmanagedResource
可能在上课之前收集了代码,但是为什么不这样buffer
呢?
使用这种反模式(实际上,最好不要只拥有托管字段,而要拥有非托管字段,而不要混合它们,然后必须对如何处理这种混合很聪明,但是but,这种模式仍然存在并且已经存在了。有时不是要处理的危险)不是不是已经收集了可管理的可管理对象(它们不会,而是被相关类中的字段保持活动状态,现在至少植根于该对象中)。最终定稿队列(如果不在其他地方)),但它们可能已由自己的定稿器定稿。或者相反,因为它们已经在完成队列中,所以它们可能在这里完成,然后再次完成。
如果您通过到达此代码,Dispose()
则它们将不会被清除(显然,假设没有错误),因为它们只有在尝试收集它们之前可以清除它们的路径才是这种方法。
如果您是通过终结器到达此代码的,则该对象已对其进行了尝试收集,并被放入终结队列中,这意味着可能仅通过它可访问的对象也已对其进行了尝试,并且如果finalisable已被排在队列中,并且不能保证哪一个排在最前面。
如果对象是一次性的但不能最终确定,则很可能又有一些字段可以最终确定,并且同样可能在该队列中。
而且,如果该对象是可抛弃的但不是可终结的,并且没有可终结的字段,那么您对该对象不执行任何操作也没关系。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句