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
  1. GC.SuppressFinalize(this); 这个方法是告诉GC一个对象已经手动释放过,并且不需要再释放了,这样的话,对象能被更早的再生。一般是紧跟在Dispose(true)之后。
  2. ~XXXXClass()析构函数,函数释放时自动执行。其中Dispose(false)程序自动调用时,只回收非托管资源。
  3. Protected virtual void Dispose(bool disposing) 标识为 Protect 是为了更好的封装,virtual是便于继承类重写。
  4. 对于某些资源的释放要做如下三部:

a)         A != null如果为空的话,不用释放。

b)         A.Dispose();

c)         A == null;

  1. GC回收资源是靠将资源标记成(Generation)代的概念,0代则直接回收,1代减一变成0代,2代回收时减一变成1代。
  2. 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模式浅析的更多相关文章

  1. 基础才是重中之重~C#中标准的IDispose模式

    回到目录 IDispose模式在C++中用的很多,用来清理资源,而在C#里,资源分为托管和非托管两种,托管资源是由C#的CLR帮助我们清理的,它是通过调用对象的析构函数完成的对象释放工作,而对于非托管 ...

  2. C#中标准的IDispose模式

    C#实现IDispose接口   .net的GC机制有两个问题:首先GC并不能释放所有资源,它更不能释放非托管资源.其次,GC也不是实时的,所有GC存在不确定性.为了解决这个问题donet提供了析构函 ...

  3. CUDA 标准编程模式

    前言 本文将介绍 CUDA 编程的基本模式,所有 CUDA 程序都基于此模式编写,即使是调用库,库的底层也是这个模式实现的. 模式描述 1. 定义需要在 device 端执行的核函数.( 函数声明前加 ...

  4. C#的内存管理原理解析+标准Dispose模式的实现

    本文内容是本人参考多本经典C#书籍和一些前辈的博文做的总结 尽管.NET运行库负责处理大部分内存管理工作,但C#程序员仍然必须理解内存管理的工作原理,了解如何高效地处理非托管的资源,才能在非常注重性能 ...

  5. [转]改善C#程序的建议4:C#中标准Dispose模式的实现

    需要明确一下C#程序(或者说.NET)中的资源.简单的说来,C#中的每一个类型都代表一种资源,而资源又分为两类: 托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象: 非托管资源:不 ...

  6. 第三篇:CUDA 标准编程模式

    前言 本文将介绍 CUDA 编程的基本模式,所有 CUDA 程序都基于此模式编写,即使是调用库,库的底层也是这个模式实现的. 模式描述 1. 定义需要在 device 端执行的核函数.( 函数声明前加 ...

  7. PHP的CLI命令行运行模式浅析

    在做开发的时候,我们不仅仅只是做各种网站或者接口,也经常需要写一些命令行脚本用来处理一些后端的事务.比如对数据进行处理统计等.当然也是为了效率着想,当一个事务有可能会有较长的耗时时,往往会交由服务器的 ...

  8. javascript订阅模式浅析和基础实例

    前言 最近在开发redux或者vux的时候,状态管理当中的createStore,以及我们在组件中调用的dispatch传递消息给状态管理中心,去处理一些操作的时候,有些类似我们常见到订阅模式 于是写 ...

  9. C#中标准Dispose模式的实现与使用(条目17 实现标准的销毁模式)

    实现了Dispose模式与实现了IDisposable接口的区别就是:IDisposable的实现的可靠性(释放相关资源)要靠编程人员来解决(你确信你从来都一直调用了Dispose(Close)方法吗 ...

随机推荐

  1. mini2440的SDRAM分析

    首先是2440的存储控制器: 暂时不管是从nand启动还是nor启动,因为我现在只关注内存,从上图可以看到由2440的Memory Controller可以寻址的范围是0x0000,0000---0x ...

  2. 使用flume的一个例子

    新项目中需要使用到hadoop和vertica,使用flume把数据加载到hadoop中,我做了一个例子, 即监控一个sharefolder,如果里面有文件,则会文件load到hadoop. 开启Fl ...

  3. [转]响应式表格jQuery插件 – Responsive tables

    本文转自:http://www.shejidaren.com/responsive-tables-for-bootstrap-3.html 这个Responsive tables jQuery插件依赖 ...

  4. cri-o pod 创建源码分析

    1. server/sandbox.go // RunPodSandbox creates and runs a pod-level sandbox func (s *Server) RunPodSa ...

  5. 《TCP/IP 详解 卷一》读书笔记 -----第四章 ARP

    1.一个物理层的网络,例如以太网,可以同时被多个不同的网络层所使用.例如网络中的一些主机使用TCP/IP协议,其他主机使用其他的网络协议. 2.设备驱动软件从不关心IP数据报中的目的IP地址.这也是为 ...

  6. 最长公共子序列模板(LCS)和LICS模板

    递归式: 实例图解: 代码: #include<stdio.h> #include<string.h> ; int dp[N][N],f[N][N]; char a[N],b[ ...

  7. Unity3D面试题汇总

    1.请描述游戏动画有哪几种,以及其原理. 2.alpha blend 工作原理 3.写光照计算中的diffuse的计算公式 4.lod是什么,优缺点是什么 5.两种阴影判断的方法工作原理 6.MipM ...

  8. JMeter学习参数化User Defined Variables与User Parameters

    偶然发现JMeter中有两个元件(User Defined Variables与User Parameters)很相近,刚开始时我也没注意,两者有什么不同.使用时却发现两者使用场景有些不同,现在小结一 ...

  9. WMI入门

    转:http://www.cnblogs.com/ceachy/archive/2013/03/21/WMI_What.html WMI入门(一):什么是WMI WMI出现至今已经二十多年了,但很多人 ...

  10. 在PLSQL中不能使用中文作为查询条件查询数据

    解决方法:  1.在oracle服务端的注册表中找到oracle-->key_oradb11g_home1,在右侧找到NLS_LANG,将其数值数据改为SIMPLIFIED CHINESE_CH ...