关于 IntPtr 等有什么特殊之处,这意味着它们可以在终结器中被触摸
本文关键字:触摸 意味着 什么 IntPtr 关于 | 更新日期: 2023-09-27 18:35:36
在标准 dispose/finalise 模式(例如 C# 中使用 Dispose() 的终结器)中,如果从终结器调用该方法,则 Dispose(bool) 不会接触托管对象,这被认为是不安全的,因为它们可能已经被垃圾回收器收集。
IntPtr 等有什么特别之处使它们安全?
作为一些背景,为了使清理代码靠近分配代码,我在分配后立即将清理操作添加到事件中,然后从 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
不是这种情况呢?
有了这种反模式(实际上,您最好只使用托管字段或仅具有非托管字段,而不是混合它们,然后必须聪明地处理这种混合,但唉,模式仍然存在,有时必须处理)危险不在于一次性托管对象可能已被收集(它们不会, 他们被相关类中的字段保持活力,现在至少从最终队列中的该对象扎根,如果不是其他地方),但他们可能已经由他们自己的终结者完成。或者相反,它们可能会在这里完成,然后再次完成,因为它们已经在最终确定队列中。
如果您通过Dispose()
到达此代码,那么它们就不会(显然假设没有错误)已被清理,因为它们仅在可以清理它们的收集尝试之前才有路径。
如果你是通过终结器到达此代码的,那么这个对象已经尝试了收集,并被放入了终结队列中,这意味着可能只能通过它访问的对象也尝试了收集,如果可完成已被放入队列中,并且不能保证哪个被放在第一位。
如果对象是一次性的但不可完成,则它很可能依次具有可完成的字段,并且同样可能位于该队列中。
如果对象是一次性的,但不可最终确定,并且没有可最终确定的字段,那么你不对它做任何事情都没有关系。
这样就可以了:
private void Dispose(bool itIsSafeToAlsoFreeManagedObjects)
{
if (_isDisposed) return;
Marshal.FreeHGlobal(buffer));
if (itIsSafeToAlsoFreeManagedObjects)
for (var i = managedObjectsToBeDisposed.Count - 1; i >= 0; i--)
{
var managedObjectToBeDisposed = managedObjectsToBeDisposed[i];
managedObjectToBeDisposed.Dispose();
managedObjectsToBeDisposed.Remove(managedObjectToBeDisposed);
}
_isDisposed = true;
}
IntPtr
是一个结构,本质上包含一个本机句柄。句柄引用的资源不会被垃圾回收器触及。结构本身也仍然有效。
我不太确定您的代码,您在终结器中使用引用类型的托管对象(附加到 OnFreeUnmanagedResource
的委托)。
编辑:阅读另一个答案后,我认为您的代码也可以,因为委托没有终结器。