.Net知识梳理---(踏踏实实学好.Net系列)

应用程序域,你真的了解吗?它可以干什么?

说到应用程序域Appdomain,我们必须要知道的知识就是进程,那么到底什么是进程呢?你不妨问问自己,怎么给进程下一个定义。到这里,我先卖个关子,你自己先想。

我们先来看一下win7中的进程:

有很多进程是吧,每一个进程都像是在执行者一个程序,或是监听或是执行或是空闲。这也是windows系统中的一个基本概念,它拥有一个运行着的程序所需要的系统资源比如分配内存。而且聪明的你一定试过,当你强行终止一个进程的时候,其他进程不会都跟着消亡,而且进程间是无法互相访问的(除非利用分布式计算方式,这都是后话了),windows系统呢,就是利用进程把工作划分为多个独立的区域的。这时候,上面的问题你应该有了答案了。对!进程就是一个程序的基本边界。

那么我们在.Net中是如何处理进程类的呢?

首先,我们要了解,System.Diagnostics这个命名空间,这里面存在一个Process类,它可以用来管理进程,比如Start,Kill,访问进程中的模块,获取进程中的线程,设定进程的优先级等。

下表显示Process常用属性。

BasePriority 获取关联进程的基本优先级。
ExitCode 获取关联进程终止时指定的值。
ExitTime 获取关联进程退出的时间。
Handle 返回关联进程的本机句柄。
HandleCount 获取由进程打开的句柄数。
HasExited 获取指示关联进程是否已终止的值。
Id 获取关联进程的唯一标识符。
MachineName 获取关联进程正在其上运行的计算机的名称。
MainModule 获取关联进程的主模块。
Modules 获取已由关联进程加载的模块。
PriorityClass 获取或设置关联进程的总体优先级类别。
ProcessName 获取该进程的名称。
StartInfo 获取或设置要传递给Process的Start方法的属性。
StartTime 获取关联进程启动的时间。
SynchronizingObject 获取或设置用于封送由于进程退出事件而发出的事件处理程序调用的对象。
Threads 获取在关联进程中运行的一组线程

除了上述属性,Process类也定义了下列常用的方法:

方法 说明
GetProcessById 创建新的 Process 组件,并将其与您指定的现有进程资源关联。
GetProcessByName 创建多个新的 Process 组件,并将其与您指定的现有进程资源关联。
GetCurrentProcess 获取新的 Process 组件并将其与当前活动的进程关联。
GetProcesses 获取本地计算机上正在运行的每一个进程列表。
Start 启动一个进程。
Kill 立即停止关联的进程。
Close 释放与此组件关联的所有资源。
WaitForExit 指示 Process 组件无限期地等待关联进程退出。

让我们写几行代码试一下

利用 Start 与Kill 方法可以简单建立或者销毁进程,下面例子就是利用 Start 方法启动记事本的进程,并打开Test.txt文件。2秒钟以后,再使用 Kill 方法销毁进程,并关闭记事本。

 static void Main(string[] args)
        {
            Process process = Process.Start("notepad.exe", "Test.txt");
            Thread.Sleep(2000);
            process.Kill();
            Console.Read();
        }

运行后打开一个记事本并且提示你是否新建Test.txt,2秒后关闭该记事本。

其他的,聪明的你可以自己去试验了。

那么上面说了一大堆,就是为了引出我们想要了解的---应用程序域,你可能会问,说了一大堆,为啥我一直在说进程?别急,慢慢往下看。

使用.Net建立的可执行程序.exe,并没有直接承载到进程中,而是承载到应用程序域(AppDomain)中了,为什么要这样呢,因为它比进程所占用的资源要少的多,我们甚至可以把它看成一个轻量级的进程。

那么就可以理解下面这段话了:在一个进程中,可以包含多个应用程序域,一个应用程序域可以装载多一个可执行程序.exe或者多个程序集.dll。这样可以使应用程序域之间实现深度隔离,即使进程中的某个应用程序域出现错误,也不会影响到其他应用程序域的正常操作。(千万不要和线程搞混了哟,区别还是蛮大的,你应该又可以理解它和线程之间的关系了),佐一张图以明理:

-----那么问题来了-------

当一个程序集同时被多个应用程序域调用时,会出现哪两种情况?:::::

---------------------------------------------------------- 第一种情况:CLR分别为不同的应用程序域加载此程序集。

-------------------------------------------------------------------------------------------- 第二种情况:CLR把此程序集加载到所有的应用程序域之外,并实现程序集共享,此情况比较特殊,被称作为Domain Neutral。(中立)

参照进程,再看看AppDomain的常用属性和方法及事件,在System命名空间当中就存在AppDomain类,用管理应用程序域。下面是AppDomain类的常用属性:

属性 说明
ActivationContext 获取当前应用程序域的激活上下文。
ApplicationIdentity 获得应用程序域中的应用程序标识。
BaseDirectory 获取基目录。
CurrentDomain 获取当前 Thread 的当前应用程序域。
Id 获得一个整数,该整数唯一标识进程中的应用程序域。
RelativeSearchPath 获取相对于基目录的路径,在此程序集冲突解决程序应探测专用程序集。
SetupInformation 获取此实例的应用程序域配置信息。

AppDomain类中有多个方法,可以用于创建一个新的应用程序域,或者执行应用程序域中的应用程序。

方法 说明
CreateDomain 创建新的应用程序域。
CreateInstance 创建在指定程序集中定义的指定类型的新实例。
CreateInstanceFrom 创建在指定程序集文件中定义的指定类型的新实例。
DoCallBack 在另一个应用程序域中执行代码,该应用程序域由指定的委托标识。
ExecuteAssembly 执行指定文件中包含的程序集。
ExecuteAssemblyByName 执行程序集。
GetAssemblies 获取已加载到此应用程序域的执行上下文中的程序集。
GetCurrentThreadId 获取当前线程标识符。
GetData 为指定名称获取存储在当前应用程序域中的值。
IsDefaultAppDomain 返回一个值,指示应用程序域是否是进程的默认应用程序域。
SetData 为应用程序域属性分配值。
Load 将 Assembly 加载到此应用程序域中。
Unload 卸载指定的应用程序域。

AppDomain类中有多个事件,用于管理应用程序域生命周期中的不同部分。

事件 说明
AssemblyLoad 在加载程序集时发生。
AssemblyResolve 在对程序集的解析失败时发生。
DomainUnload 在即将卸载 AppDomain 时发生。
ProcessExit 当默认应用程序域的父进程存在时发生。
ReflectionOnlyAssemblyResolve 当程序集的解析在只反射上下文中失败时发生。
ResourceResolve 当资源解析因资源不是程序集中的有效链接资源或嵌入资源而失败时发生。
TypeResolve 在对类型的解析失败时发生。
UnhandledException 当某个异常未被捕获时出现。

实战一下------------------------------------------------------------------------------------------>

可以看到,通过CreateDomain方法可以建立一个新的应用程序域。 下面的例子将使用CreateDomain建立一个应用程序域,并使用Load方法加载程序集System.Web.Http.dll(我随便找来的一个,默认在bin文件夹里找,记住咯)。最后使用GetAssemblies方法,列举此应用程序域中的所有程序集。

  static void Main(string[] args)
        {
            var appDomain = AppDomain.CreateDomain("Test");
            appDomain.Load("System.Web.Http");
            Parallel.ForEach(appDomain.GetAssemblies(), assembly =>
            {
                Console.WriteLine(assembly.FullName);
            });

            Console.Read();
        }

结果:

但是要注意的是,一旦加载了程序集,是没法直接卸载它的,必须把承载的AppDomain整个卸载才行。

看实战

通过Unload可以卸载AppDomain,在AppDomain卸载时将会触发DomainUnload事件。
下面的例子中,将会使用CreateDomain建立一个名为NewAppDomain的应用程序域。然后建立AssemblyLoad的事件处理方法,在程序集加载时显示程序集的信息。最后建立DomainUnload事件处理方法,在AppDomain卸载时显示卸载信息。

static void Main(string[] args)
        {
            AppDomain newAppDomain = AppDomain.CreateDomain("NewAppDomain");
            //建立AssemblyLoad事件处理方法
            newAppDomain.AssemblyLoad += (obj, e) =>
            {
                Console.WriteLine(string.Format("{0} is loading", e.LoadedAssembly.GetName()));
            };
            //建立DomainUpload事件处理方法
            newAppDomain.DomainUnload += (obj, e) =>
                {
                    Console.WriteLine("NewAppDomain Unload");
                };
            //加载程序集
            newAppDomain.Load("ChnCharInfo");
            //卸载AppDomain
            AppDomain.Unload(newAppDomain);
            Console.Read();
        }

结果:

------------------------------------------------------------------------

为了加深印象,再来一例:建立程序集中指定类的对象

使用CreateInstance方法,能建立程序集中指定类的对像。但使用此方法将返回一个ObjectHandle对象,若要将此值转化为原类型,可调用Unwrap方法。
下面例子会建立Model.dll程序集中的Model.Person类。

     static void Main(string[] args)
        {
            var person = (Person)AppDomain.CurrentDomain.CreateInstance("Model", "Model.Person").Unwrap();
            person.ID = 1;
            person.Name = "test";
            person.Age = 29;
            Console.WriteLine(string.Format("{0}‘s age is {1}",person.Name,person.Age));
            Console.Read();
        }

结果:


Model.dll

namespace Model
{
    public class Person
    {
        public int ID
        { get; set; }
        public string Name
        { get; set; }
        public int Age
        { get; set; }
    }
}

这时候你应该知道了什么是应用程序域,那么是不是就结束了呢?当然不是!你还应该知道一个更细粒度的.Net上下文。下面我们通过一句话来阐述三者的关系。

进程可以承载一组相关的.Net程序集,而应用程序域是对该进程的逻辑细分。一个应用程序域进一步被细分成多个上下文边界,这些边界用来分组目的相似的.Net对象。使用上下文的概念,CLR便能够确保恰当地控制那些带特殊运行时要求的对象。

那么上下文到底可以干什么:

1:上下文用来承载.Net对象的实体,所有的.Net对象都存在于Context中。

2:每个AppDomain当中至少存在一个默认Context(Context 0)

一般情况下, 不要要指定特定上下文的对象被称为上下文灵活对象(Context-agile),建立此对象不需要特定的操作,只需要由CLR自行管理,一般这些对象都会被建立在Context 0中。

时间: 2024-10-15 11:06:11

.Net知识梳理---(踏踏实实学好.Net系列)的相关文章

Js基础知识梳理系列

小序:总是感觉自己的技术一直在原地踏步,想学习一些新的技术,但学起来很吃力,而且总是没有什么实际的收获,似乎进入了所谓的“瓶颈期”.问了一些前辈是否也遇到过同样的问题,他们给我的解决方案是,基础不稳固,再把基础巩固一下.然后学起新的技术会轻松一点点.也是,反思自己曾经走马观花式的学习,确实有很多细腻的技术点没有接触到,所以,我还没有做到大神眼里的“精”.抱着这样的目的,再次翻开<JavaScript高级编程设计>,我将按照着书上的内容和顺序,系统化的学习这本书中的知识.并且在学习的过程中会结合

[SQL] SQL 基础知识梳理(一)- 数据库与 SQL

SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 序 目录 What's 数据库 数据库结构 SQL 概要 创建表 删除和更新表 1-1 What's 数据库 1.数据库(Database,DB):将大量数据保存起来,通过计算机加工而成的可以进行高效访问的数据集合.如:大型-银行存储的信息,小型-电话簿. 2.数据库管理系统(Batabase Management Syste

[SQL] SQL 基础知识梳理(七)- 集合运算

SQL 基础知识梳理(七)- 集合运算 目录 表的加减法 联结(以列为单位) 一.表的加减法 1.集合:记录的集合(表.视图和查询的执行结果). 2.UNION(并集):表的加法 -- DDL:创建表 CREATE TABLE Shohin2 (shohin_id CHAR(4) NOT NULL, shohin_mei VARCHAR(100) NOT NULL, shohin_bunrui VARCHAR(32) NOT NULL, hanbai_tanka INTEGER , shiire

[SQL] SQL 基础知识梳理(六)- 函数、谓词、CASE 表达式

SQL 基础知识梳理(六)-  函数.谓词.CASE 表达式 目录 函数 谓词 CASE 表达式 一.函数 1.函数:输入某一值得到相应输出结果的功能,输入值称为“参数”,输出值称为“返回值”. 2.函数的种类: (1)算术函数 - 数值计算 (2)字符串函数 - 字符串操作 (3)日期函数 - 日期操作 (4)转换函数 - 转换数据类型 (5)聚合函数 - 数据聚合 3.算术函数(加.减.乘.除):+.-.*./ [备注]数据类型 NUMBERIC(全体位数,小数位数)可以指定数值的大小. C

指针知识梳理7- 函数指针

一.函数的地址 前面讲 程序运行起来以后,在内存中有代码区,程序运行每一条指令,是从内存中读出来这条指令,然后再运行. 所谓函数的地址是指函数的入口地址,这个函数的从这个地址開始进入运行,也就是从这个地址处取指令运行. 那么在代码层面,函数的地址用 函数指针变量 来存储. 二.基本使用 1.函数指针定义 函数指针的定义,在语法看起来略微有点怪,仅仅须要记住形式 返回值 (*指针变量名)(形參类型): 比方,下面4个函数 void func1(void) { } int func2(void) {

JavaScript基础知识梳理--数组

JavaScript基础知识梳理--数组 1.创建方法 空数组:var obj=new Array(); 指定长度数组: var obj=new Array( size ); 指定元素数组 :  var obj=new Array( 元素1,元素2,....): 单位数组:var obj=new Array[ 元素1,元素2,元素3,...,元素N]; 多维数组:var a=new Array( [数组1],[数组2],[数组3],...,[数组N] ); 2.基本操作 存取数组元素: 单维数组

Java基础知识梳理《一》

一.Java数据类型(简单称之为“四类八种”) java 基本的数据类型长度都是固定的,好处是在实现跨平台时就统一了. 1.整型 byte short int long (分别是1,2,4,8个字节) 类型 存储需求 位数 取值范围 byte 1字节 8位 -128~127 short 2字节 16位 -2^15 ~2^15-1 int 4字节 32位 -2^31~2^31-1 long 8字节 64位 -2^63~2^63-1 当超出int表示范围时,应该使用long型,添加后缀一大写的L 注

struts2 知识梳理

一:struts.xml配置详解: 1.<include> 表示引入其他配置文件 2.<constant> 定义常量 3.<package>:  属性 是否必需 描述name 是 包名,作为其它包应用本包的标记extends 否 设置本包继承其它包namespace 否 设置包的命名空间,会改变url,abstact 否 设置为抽象包 4<action>和<result> <action>有name,class,method,conv

指针知识梳理6-const与指针

const 定义的变量为只读变量,在语法层面上通过这个变量去修改内存是不允许的. 但是对于以下代码,就有很多人绕了: const int  *p1;  //p1能变,*p1不能变 int const  *p2;  //p2能变,*p2不能变 int *const  p3;  //p3不能变,*p2能变 我们通过代码来验证说明这三种写法: </pre><p></p><pre> #include <stdio.h> int main() { int