标准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)方法吗 ...
随机推荐
- Less里css表达式的写法
项目中用的grunt-contrib-less, 写了以下less代码 .mapfix{ position: fixed; top:10px; width: 430px; z-index: 100; ...
- Syslog
一.简介 syslog是一种工业标准的协议,可用来记录设备的日志.在UNIX系统,路由器.交换机等网络设备中,系统日志(System Log)记录系统中任何时间发生的大小事件.管理者可以通过查看系统记 ...
- hdu 2196 Computer(树形DP)
Computer Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- OO的设计原则
今天同事和我们一起讨论分享了OO的设计原则,讨论使人明晰,有人一起讨论学习是一件幸福的事情. 1.开闭原则 对功能的扩展是开放的,对修改是闭合的. 可以应用于类的设计,框架的设计等. 为什么?开闭原则 ...
- html5 实现video标签的自定义播放进度条
现在随着html5的渐热,越来越多的web开发者都开始选择使用html5写出一些比较好的web应用. html代码: <!DOCTYPE html> <html lang=" ...
- Codeforces Round #258 E Devu and Flowers --容斥原理
这题又是容斥原理,最近各种做容斥原理啊.当然,好像题解给的不是容斥原理的方法,而是用到Lucas定理好像.这里只讲容斥的做法. 题意:从n个容器中总共取s朵花出来,问有多少种情况.其中告诉你每个盒子中 ...
- TestLink学习三:发送邮件的两种配置方法
第一种:修改config.inc.php中的[smtp],配置为默认本地发送,用hotmail用户做接收,调试成功!(本人未尝试这种) // ----------------------------- ...
- Java Executor并发框架(一)整体介绍
一.概述 Java是天生就支持并发的语言,支持并发意味着多线程,线程的频繁创建在高并发及大数据量是非常消耗资源的,因为java提供了线程池.在jdk1.5以前的版本中,线程池的使用是及其简陋的,但是在 ...
- C++容器的复制
C++容器的复制不同于Java Java是引用复制,复制的仅仅是对象的引用, 在需要复制容器内对象的副本集合的情况,需要使用Clone方法,而且要注意clone方法的浅拷贝 深拷贝 C++的容器复制 ...
- Java的IO操作---File类
目标 1)掌握File类作用 2)可以使用file类中方法对文件进行读写操作. File类 唯一与文件有关的类.使用file类可进行创建或删除操作,要想使用File类,首先观察File类的构造方法. ...