标准IDispose模式浅析
DoNet资源
众所周知,.Net内存管理分托管资源和非托管资源,把内存中的对象按照这两种资源划分,然后由GC负责回收托管资源(Managed Resource),而对于非托管资源来讲,就需要程序员手动释放。
Framework的设计者的本意是降低Developer的入门难度,提高开发效率,让使用者更少的关注“垃圾回收”,但也正是如此的封装,才导致越来越多的滥用,甚至可怕的效率低下。(这里,我本人强调的是对DoNet垃圾回收机制的理解程度导致的滥用现象。)常常有人抱怨资源一直没有被回收,或者没有按照时间点回收,或者创建再生资源时,效率低下。(“再生资源”指刚刚被完全释放的资源,依据《.Net设计规范》中的说法,不适当的回收导致在创建时的效率低下。)
说了这些就是想提醒大家,我们要有一个更正确,更完善的方式回收资源。
IDisposable接口
在Framework的设计中很多地方都实现了IDisposable接口的Dispose方法,GC会自动地,随机地调用某个资源的析构方法,从而达到自动回收垃圾的目的,该接口来自于System命名空间下。
所以,对于我们自定义的类来讲一般都要实现IDisposable接口,已完成自动回收。
标准IDispose模式
不罗嗦了,网上相关的资料很多,这里写下来也是让自己更熟悉这种模式,毕竟理解是一回事,讲解出来又是另一回事。
首先引用IDisposable接口,然后,没有然后了,直接贴代码:
#region 标准IDispose模式
Private bool disposed = false;
Public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~XXXXClass()
{
Dispose(false);
}
Protected virtual void Dispose(bool disposing)
{
If(disposed)
Return;
If(disposing)
{
If(Resource != null)
{
// Release managed resource
}
}
// Release unmanaged resource
disposed = true;
}
#endregion
- GC.SuppressFinalize(this); 这个方法是告诉GC一个对象已经手动释放过,并且不需要再释放了,这样的话,对象能被更早的再生。一般是紧跟在Dispose(true)之后。
- ~XXXXClass()析构函数,函数释放时自动执行。其中Dispose(false)程序自动调用时,只回收非托管资源。
- Protected virtual void Dispose(bool disposing) 标识为 Protect 是为了更好的封装,virtual是便于继承类重写。
- 对于某些资源的释放要做如下三部:
a) A != null如果为空的话,不用释放。
b) A.Dispose();
c) A == null;
- GC回收资源是靠将资源标记成(Generation)代的概念,0代则直接回收,1代减一变成0代,2代回收时减一变成1代。
- GC.Collect()就是手动将资源标记的方法,但是即使是0代也未必立即回收释放。
这里关于Generation的概念再补充一下(谢谢@大家都不容易 的提醒):
引入MSDN的解释:
It is recommended, but not required, that garbage collectors support object aging using generations. A generation is a unit of measure of the relative age of objects in memory. The generation number, or age, of an object indicates the generation to which an object belongs. Objects created more recently are part of newer generations, and have lower generation numbers than objects created earlier in the application life cycle. Objects in the most recent generation are in generation 0.
http://msdn.microsoft.com/en-us/library/system.gc(v=vs.110).aspx
一下子还真找不出更多的资料,因为这些概念都是从各种各样的书中获得的知识,然后加以自己的理解。其中《编写高质量的代码---改善C#程序的157个建议》中有阐述这个问题。
另:纠正下自己的英文错误,是Generation而不是Generate,慢慢来吧。
针对线程安全问题的优化
首先十分感谢@冰麟轻武的提醒,之前还真没有考虑到线程安全的问题,因为这方面的应用场景还没有遇到,不过还是学习了@冰麟轻武的Dispose模式之后受益匪浅。
线程安全是指多个线程同时操作一个实例(Instance)使用其资源时产生的共享资源状态问题,也就是说线程A/B释放了M的资源,而线程B/A同时也要求释放资源,这样导致资源重复释放,严重时调用了空对象的方法,导致错误,所以,如果是以上场景的话,就要考虑线程安全这个问题。
我个人又对@冰麟轻武这位仁兄的方法(https://code.csdn.net/snippets/112056)进行了修改,如果有错误的地方还请各位看官不吝指出。
/*************************************************************
* Copyright @ BOCO Group
* All Rights Reserved.
* Author: Cui Yansong(STEPHEN-PC.Cuiyansong)
* Mail: cuiyansong@boco.com.cn
* Create Date: 5/23/2014 9:29:46 AM
* File Name: DisposePatternBase
* CLR Version: 4.0.30319.17929
* ***********************************************************/ using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text; namespace BocodeProtobufTest.Common
{
/* ******************************************************************************************
* Note 1: 保证线程安全
* System.Threading.Interlocked.CompareExchange(T,T,T)此方法是为了保证线程安全所执行的原子操作。
* http://msdn.microsoft.com/en-us/library/bb297966.aspx
* 首先将disposedMark标记为已释放,保证其他线程获得disposedMark值为已释放,其他线程将不执行该操作。
* 然后判断disposedMark标记之前的值是否为未释放,如果是则执行Dispose。
*
* Note 2: 区分托管资源和非托管资源的释放方法
* 如果执行手动释放(直接调用类的Dispose方法),则需要将所有资源均释放调;
* 如果程序执行析构函数,则无需释放托管资源,原因在于大多数情况下托管资源可自动释放并且当系统执行析
* 构方法时,已无法手动调用Dispose方法。
* ******************************************************************************************/
public abstract class DisposePatternBase : IDisposable
{
#region Public Properties #endregion Public Properties #region Private Properties
/// <summary>
/// A private property indicate whether resource have been diposed.
/// </summary>
/// <remarks>
/// 0 --- Not Released
/// 1 --- Released
/// </remarks>
private int disposedMark = ; #endregion Private Properties #region Constructor
/// <summary>
/// 系统销毁对象时自动调用
/// </summary>
~DisposePatternBase()
{
// 原子操作,保证线程安全。
var original = System.Threading.Interlocked.CompareExchange(ref disposedMark, , );
if (original != ) return;
Dispose(false);
} #endregion Constructor #region Public Method
/// <summary>
/// 外部调用(手动)Dispose方法
/// </summary>
public void Dispose()
{
// 原子操作,保证线程安全。
var original = System.Threading.Interlocked.CompareExchange(ref disposedMark, , );
if (original != ) return;
Dispose(true);
GC.SuppressFinalize(this);
} protected abstract void Dispose(bool disposing); #endregion Public Method
} public class DisposePatternDemo : DisposePatternBase
{
private SafeHandle handle; private IList<byte> source; protected override void Dispose(bool disposing)
{
if (disposing)
{
// Release managed resource
if (source != null && source.Count != )
{
source = null;
}
} // Release unmanaged resource
if (handle != null)
handle.Dispose();
}
}
}
Reference
《.Net设计规范》(Framework Design Guideline)
http://msdn.microsoft.com/zh-cn/library/System.GC(v=vs.80).aspx
https://code.csdn.net/snippets/112056
http://baike.baidu.com/view/1298606.htm?fr=aladdin
标准IDispose模式浅析的更多相关文章
- 基础才是重中之重~C#中标准的IDispose模式
回到目录 IDispose模式在C++中用的很多,用来清理资源,而在C#里,资源分为托管和非托管两种,托管资源是由C#的CLR帮助我们清理的,它是通过调用对象的析构函数完成的对象释放工作,而对于非托管 ...
- C#中标准的IDispose模式
C#实现IDispose接口 .net的GC机制有两个问题:首先GC并不能释放所有资源,它更不能释放非托管资源.其次,GC也不是实时的,所有GC存在不确定性.为了解决这个问题donet提供了析构函 ...
- CUDA 标准编程模式
前言 本文将介绍 CUDA 编程的基本模式,所有 CUDA 程序都基于此模式编写,即使是调用库,库的底层也是这个模式实现的. 模式描述 1. 定义需要在 device 端执行的核函数.( 函数声明前加 ...
- C#的内存管理原理解析+标准Dispose模式的实现
本文内容是本人参考多本经典C#书籍和一些前辈的博文做的总结 尽管.NET运行库负责处理大部分内存管理工作,但C#程序员仍然必须理解内存管理的工作原理,了解如何高效地处理非托管的资源,才能在非常注重性能 ...
- [转]改善C#程序的建议4:C#中标准Dispose模式的实现
需要明确一下C#程序(或者说.NET)中的资源.简单的说来,C#中的每一个类型都代表一种资源,而资源又分为两类: 托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象: 非托管资源:不 ...
- 第三篇:CUDA 标准编程模式
前言 本文将介绍 CUDA 编程的基本模式,所有 CUDA 程序都基于此模式编写,即使是调用库,库的底层也是这个模式实现的. 模式描述 1. 定义需要在 device 端执行的核函数.( 函数声明前加 ...
- PHP的CLI命令行运行模式浅析
在做开发的时候,我们不仅仅只是做各种网站或者接口,也经常需要写一些命令行脚本用来处理一些后端的事务.比如对数据进行处理统计等.当然也是为了效率着想,当一个事务有可能会有较长的耗时时,往往会交由服务器的 ...
- javascript订阅模式浅析和基础实例
前言 最近在开发redux或者vux的时候,状态管理当中的createStore,以及我们在组件中调用的dispatch传递消息给状态管理中心,去处理一些操作的时候,有些类似我们常见到订阅模式 于是写 ...
- C#中标准Dispose模式的实现与使用(条目17 实现标准的销毁模式)
实现了Dispose模式与实现了IDisposable接口的区别就是:IDisposable的实现的可靠性(释放相关资源)要靠编程人员来解决(你确信你从来都一直调用了Dispose(Close)方法吗 ...
随机推荐
- hdu Flow Problem (最大流 裸题)
最大流裸题,贴下模版 view code#include <iostream> #include <cstdio> #include <cstring> #incl ...
- spark加载hadoop本地库的时候出现不能加载的情况要怎么解决呢?
hadoop shell运行的时候不会报这个错误,因为我已经重新在64位机上编译了源文件,并把so文件复制到hadoop的native目录下,而且环境变量也设置正确了,所以hadoop本身没有问题. ...
- WEB安全--渗透笔记
前言 服务器被攻击是常有的事,自从上一次被注入挂马后最先想到的是安全狗,最新版的安全狗软件可以抵挡大部分的恶意攻击,但是却有很多网站管理员疏于管理,才有了接下来的入侵. 我们的攻击目标近期上线了安全狗 ...
- At least one object must implement IComparable
中文:必须至少有一个对象实现 IComparable. 序列排序时报这个错误 lstReports.OrderBy(r => new { r.DepartmentName, r.ReportNo ...
- 我的第一篇博客--SQL小语句
开通了博客,拥有了属于自己的小小天地.先写一篇今儿刚学到的 1 remove mirroring relationship alter database datab_name set partner ...
- react webpack.config.js 入门学习
在学习react 的时候必然会用到webpack打包工具,webpack的快速入门另外一篇文章中有记录,这里只记录webpack.config.js文件,因为每个项目下都必须配置,通俗的讲,它的作用就 ...
- org.springframework.web.context.ContextLoaderListen 报错解决办法
今天搭建SSH项目的时候出现了如下错误: 严重: Error configuring application listener of class org.springframework.web.con ...
- Windows环境中Openfire与Spark安装与配置指南
安装软件: openfire3.9.3 spark2.6.3 安装环境: WindowsXP JDK1.6.0_21 Oracle 一.openfire安装 1.安装openfire3.9.3,下载地 ...
- Android Activity的生命周期
一.为什么要了解Activity的生命周期 activity is directly affected by its association withother activities, its tas ...
- $apply方法的作用
$apply方法是用来触发脏检查,它在控制器里监听一个变量,每当这个变量的值改变的时候,它会去与最初的值做一次比较,然后HTML页面就会及时更新该变量的值(将最新的值赋值到html页面的view层或M ...