重学c#系列——非托管实例(五)
2020-12-26 06:28
标签:补齐 code 变化 class tps arp oid 因此 编写 托管资源到是好,有垃圾回收资源可以帮忙,即使需要我们的一些小小的调试来优化,也是让人感到欣慰的。但是非托管资源就显得苍白无力了,需要程序员自己去设计回收,同样有设计的地方也就能体现出程序员的设计水平。 托管类在封装对非托管资源的直接引用或者间接引用时,需要制定专门的规则,确保非托管资源在回收类的一个实例时释放。 为什么要确保呢? 是这样子的,画一个图。 上图中托管中生成并引用非托管,一但非托管和托管中的引用断开(托管资源被回收),那么这个时候非托管资源还在,那么释放这个问题就有一丢丢困难。 常见的有两种机制来自动释放非托管资源。 声明一个构析函数作为一个类的一个成员。 在类中实现System.IDisposable. 好的,接下来就开始看例子吧。 先从构析函数看起吧。 在IL中是这样子的。 简单介绍一下这个Finalize 是一个终结器,我们无法重写,文档中原文是这样子的。 这个SafeHandle 是啥呢?是安全句柄。这东西学问很大,非该文重点,先可以理解为句柄即可。 这里简单介绍一下句柄。 职业盗图: 再次职业盗图: 假设有一个句柄为0X00000AC6。有一个区域存储这各个对象的地址,0X00000AC6指向这个区域里面的区域A,A只是这个区中的一个。这个A指向真实的对象在内存中的位置。 这时候就有疑问了,那么不是和指针一个样子吗?唯一不同的是指针的指针啊。是的,就是指针的指针。但是为啥要这么做呢? 是这样子的,对象在内存中的位置是变化的,而不是不变的。我们有时候看到电脑下面冒红灯,这时候产生了虚拟内存,实际就是把硬盘当做内存了。但是我们发现电脑有点卡后,但是程序没有崩溃。 当对象内存写入我们的硬盘,使用的时候又读出来了,这时候内存地址是变化了。这时候在内存中的操作是区域A的值变化了,而句柄的值没有变化,因为它指向区域A。 现在我们通过实现构析函数来实现释放非托管资源,那么这种方式怎么样呢?这种方式是存在问题的,所以现在c#的构析函数去释放非托管谈的也不多。 主要问题如下: 无法确认构析函数何时执行,垃圾回收机制不会马上回收这个对象,那么也就不会立即执行构析函数。 构析函数的实现会延迟该对象在内存中的存在时间。没有构析函数的对象,会在垃圾回收器中一次处理从内存中删除,实现构析函数的对象需要两次。 然后所有对象的终结器是由一个线程来完成的,如果Finalize中存在复杂的业务操作,那么系统性能下降是可以预见的。 看例子: 然后只要用完调用Dispose即可。 但是可能有时候程序员忘记主动调用了Dispose。 所以改成这样。 那么是否这样就结束了呢? 不是的。 文档中这样介绍道:任何非密封类都应具有要实现的附加 Dispose(bool) 重载方法。 为什么这样说呢?因为是这样子的,不是密封类,那么可能会成为某个类的基类,那么子类就要考虑基类如何释放啊,所以加一个重载方法。 看下思路: Dispose(bool) 方法重载 方法的主体包含两个代码块: 那么为什么明确去释放实现IDisposable 的托管资源呢? 文档中回答是这样子的: 给个完整例子。 _safeHandle 和 Resource 一样同样可以通过构析函数去释放非托管,但是呢,如果自己Resource 主动Dispose去释放,那么最好把它的子对象(托管)的Dispose给执行了,好处上面写了。 那么这时候为什么在构析函数中为显示为false呢?因为构析函数这时候本质是在终结器中执行,属于系统那一套,有太多不确定因素了,所以干脆_safeHandle 自己去调用自己析构函数。 后来我发现.net core和.net framework,他们的构析函数执行方式是不一样的。 举个栗子: 在.net framework 中马上回去调用构析函数,但是在.net core中并不会,等了几分钟没有反应。 原因可以在: https://github.com/dotnet/corefx/issues/5205 知道了大概怎么回事。 好的,回到非托管中来。 那么继承它的子类怎么写呢? 非托管有太多的东西了,比如说异步dispose,using。在此肯定整理不完,后续另外一节补齐。 后一节,异步。 重学c#系列——非托管实例(五) 标签:补齐 code 变化 class tps arp oid 因此 编写 原文地址:https://www.cnblogs.com/aoximin/p/13377264.html前言
正文
构析函数
class Resource
{
~Resource()
{
//释放资源
}
}
protected override void Finalize()
{
try
{
//构析函数写的
}
finally
{
base.Finalize();
}
}
从包装非托管资源的 SafeHandle 派生的类(推荐),或对 Object.Finalize 方法的重写。 SafeHandle 类提供了终结器,因此你无需自行编写。
实现IDisposable
class Resource : IDisposable
{
public void Dispose()
{
//释放资源
}
}
class Resource : IDisposable
{
bool _isDisposed=false;
public void Dispose()
{
//释放资源
_isDisposed = true;
//标志不用掉析构函数
GC.SuppressFinalize(this);
}
~Resource()
{
if (_isDisposed)
{
return;
}
this.Dispose();
}
}
注:从终结器调用时,disposing 参数应为 false,从 IDisposable.Dispose 方法调用时应为 true。 换言之,确定情况下调用时为 true,而在不确定情况下调用时为 false。
class Resource : IDisposable
{
bool _isDisposed=false;
public void Dispose()
{
//释放资源
Dispose(true);
//标志不用掉析构函数
GC.SuppressFinalize(this);
}
~Resource()
{
this.Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
if (disposing)
{
//释放托管相关资源
}
//释放非托管资源
_isDisposed = true;
}
}
释放非托管资源的块。 无论 disposing 参数的值如何,都会执行此块。
释放托管资源的条件块。 如果 disposing 的值为 true,则执行此块。 它释放的托管资源可包括:
实现 IDisposable 的托管对象。 可用于调用其 Dispose 实现(级联释放)的条件块。 如果你已使用 System.Runtime.InteropServices.SafeHandle 的派生类来包装非托管资源,则应在此处调用 SafeHandle.Dispose() 实现。
占用大量内存或使用短缺资源的托管对象。 将大型托管对象引用分配到 null,使它们更有可能无法访问。 相比以非确定性方式回收它们,这样做释放的速度更快。
如果你的类拥有一个字段或属性,并且其类型实现 IDisposable,则包含类本身还应实现 IDisposable。 实例化 IDisposable 实现并将其存储为实例成员的类,也负责清理。 这是为了帮助确保引用的可释放类型可通过 Dispose 方法明确执行清理。
class Resource : IDisposable
{
bool _isDisposed=false;
private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);
public void Dispose()
{
//释放资源
Dispose(true);
//标志不用掉析构函数
GC.SuppressFinalize(this);
}
~Resource()
{
this.Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
if (disposing)
{
_safeHandle?.Dispose();
//释放托管相关资源
}
//释放非托管资源
_isDisposed = true;
}
}
static void Main(string[] args)
{
{
Resource resource = new Resource();
}
GC.Collect();
Console.Read();
}
class ResourceChild: Resource
{
bool _isDisposed = false;
~ResourceChild()
{
Dispose(false);
}
protected override void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
if (disposing)
{
//释放托管相关资源
}
//释放非托管资源
_isDisposed = true;
base.Dispose();
}
}
结