标准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)方法吗 ...
随机推荐
- Linux iptables
一.简介 http://liaoph.com/iptables/ 二.操作 1)查看规则 iptables -t filter -L -n iptables -t nat -L -n iptables ...
- Unity CombineChildren和MeshCombineUtility
原理 Unity3D如何通过CombineChildren和MeshCombineUtility优化场景? 首先解释下联结的原理和意思:文档里说,显卡对于一个含100个面片的物体的和含1500个面片的 ...
- QTP 10 安装及破解
QTP(QuickTest Professional),是一款比较优秀的商业自动化测试工具,主要用于web项目和C/S结构程序的测试. QTP具有的一大特性:关键字驱动测试(keyword-drive ...
- 考虑与Maya结合
今天改进了Hessian各块的计算代码,减少了一些内存操作.下一步准备把模拟平台与Maya结合,这样就可以利用Maya丰富的变形算法了. 这一步需要考虑以下问题: 1.把场景设置为某一帧.这一点可以用 ...
- Python-执行系统命令
执行系统命令 os.system os.spawn* os.popen popen2.* commands.* 后面三个已经废弃,以上执行shell命令的相关的模块和函数的功能均在subprocess ...
- 各浏览器对typeof运算符的实现差异
1,IE6/7/8中typeof运算符对BOM对象如window,document,location,history等对象的方法返回“object”,标准浏览器都返回“function”. 1 2 3 ...
- Java反射机制的学习
Java反射机制是Java语言被视为准动态语言的关键性质.Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的相关信息,动态地生成此类,并调 ...
- pandas 透视表 pivot_table
The function pandas.pivot_table can be used to create spreadsheet-style pivot tables. It takes a num ...
- MVC3学习:利用mvc3+ajax实现登录
用到的工具或技术:vs2010,EF code first,JQuery ajax,mvc3. 第一步:准备数据库. 利用EF code first,先写实体类,然后根据实体类自动创建数据库:或者先创 ...
- matlab如何连同换行也输入txt中
\r是回车符,\n是换行符,两者结合方能在txt显示为换行 fidID = fopen('test.txt', 'w+'); str='string'; fprintf(fidID,'%s \r\n' ...