关于appdomain, assembly, 进程,线程的概念体会

AppDomain是CLR的运行单元,它可以加载Assembly、创建对象以及执行程序。

AppDomain是CLR实现代码隔离的基本机制。

每一个AppDomain可以单独运行、停止;每个AppDomain有自己默认的异常处理;

一个AppDomain的运行失败不会影响到其他的AppDomain。

CLR在被CLR Host(windows shell or InternetExplorer or SQL Server)加载后,要创建一个默认的AppDomain,程序的入口点

(Main方法)就是在这个默认的AppDomain中执行。

1.AppDomain vs 进程

AppDomain被创建在进程中,一个进程内可以有多个AppDomain。一个AppDomain只能属于一个进程。

2.AppDomain vs 线程

其实两者本来没什么好对比的。AppDomain是个静态概念,只是限定了对象的边界;线程是个动态概念,它可以运行在不同的

AppDomain。

一个AppDomain内可以创建多个线程,但是不能限定这些线程只能在本AppDomain内执行代码。

CLR中的System.Threading.Thread对象其实是个soft thread,它并不能被操作系统识别;操作系统能识别的是hard thread。

一个soft thread只属于一个AppDomain,穿越AppDomain的是hard thread。当hard thread访问到某个AppDomain时,一个

AppDomain就会为之产生一个soft thread。

hard thread有thread local storage(TLS),这个存储区被CLR用来存储这个hard thread当前对应的AppDomain引用以及soft

thread引用。当一个hard thread穿越到另外一个AppDomain时,TLS中的这些引用也会改变。

当然这个说法很可能是和CLR的实现相关的。

3.AppDomain vs Assembly

Assembly是.Net程序的基本部署单元,它可以为CLR提供用于识别类型的元数据等等。Assembly不能单独执行,它必须被加载到

AppDomain中,然后由AppDomain创建程序集中的对象。

一个Assembly可以被多个AppDomain加载,一个AppDomain可以加载多个Assembly。

每个AppDomain引用到某个类型的时候需要把相应的assembly在各自的AppDomain中初始化。因此,每个AppDomain会单独保持一

个类的静态变量。

4.AppDomain vs 对象

任何对象只能属于一个AppDomain。AppDomain用来隔离对象,不同AppDomain之间的对象必须通过Proxy(reference type)或者

Clone(value type)通信。

引用类型需要继承System.MarshalByRefObject才能被Marshal/UnMarshal(Proxy)。

值类型需要设置Serializable属性才能被Marshal/UnMarshal(Clone)。

5.AppDomain vs Assembly Code

AppDomain和程序集的源代码是什么关系呢?每个程序集的代码会分别装载到各个AppDomain中?

首先我们要把程序集分3类

1.mscorlib,这是每个.net程序都要引用到的程序集。

2.GAC,这个是强命名的公用程序集,可以被所有的.net程序引用。

3.Assembly not in GAC,这是普通的assembly,可以不是强命名,不放到GAC中。

启动CLR,进入entry point时可以设置LoaderOptimization属性:

[LoaderOptimization(LoaderOptimization.MultiDomain]

static void Main()

{...}

LoaderOptimization属性可以设置三个不同的枚举值,来设置针对前面说的三种程序集的代码存放以及访问方式。

LoaderOptimization Enumeration/Attribute

ValueExpected Domains in ProcessEach Domain Expected to Run ...Code for MSCORLIBCode for Assemblies in GACCode for Assemblies not in GAC

SingleDomainOneN/APer-processPer-domainPer-domain

MultiDomainManySame ProgramPer-processPer-processPer-process

MultiDomainHostManyDifferent ProgramsPer-processPer-processPer-domain

1.SingleDomain,由于只启动一个AppDomain,那么code就被直接装载到了AppDomain中,访问静态变量更快捷。

2.MultiDomain,所有的Assembly代码是进程级别的,因此所有的AppDomain只访问一份代码。这大大减少了程序占用的内存,但

是由于程序集的静态变量仍然在各个AppDomain中,因此代码访问静态变量需要先得到AppDomain的引用再进行转换,速度会受到

影响。

3.MultiDomainHost,只有GAC代码是共享的,非GAC的Assembly依然会加载到被使用的AppDomain中,这样提高了静态变量的访问

速度,当然也增加了程序占用的内存。

不管是哪种方式,mscorlib始终是process级别的,即只有一份mscorlib代码在内存中。

C#中动态加载和卸载DLL

在C++中加载和卸载DLL是一件很容易的事,LoadLibrary和FreeLibrary让你能够轻易的在程序中加载DLL,然后在任何地方 卸载。在C#中我们也能使用Assembly.LoadFile实现动态加载DLL,但是当你试图卸载时,你会很惊讶的发现Assembly没有提供任何 卸载的方法。这是由于托管代码的自动垃圾回收机制会做这件事情,所以C#不提供释放资源的函数,一切由垃圾回收来做。

这引发了一个问题,用Assembly加载的DLL可能只在程序结束的时候才会被释放,这也意味着在程序运行期间无法更新被加载的DLL。而这个功能在某 些程序设计时是非常必要的,考虑你正在用反射机制写一个查看DLL中所有函数详细信息的程序,程序提供一个菜单让用户可以选择DLL文件,这时就需要让程 序能够卸载DLL,否则一旦用户重新得到新版本DLL时,必须要重新启动程序,重新选择加载DLL文件,这样的设计是用户无法忍受的。

C#也提供了实现动态卸载DLL的方法,通过AppDomain来实现。AppDomain是一个独立执行应用程序的环境,当AppDomain被卸载的 时候,在该环境中的所有资源也将被回收。关于AppDomain的详细资料参考MSDN。下面是使用AppDomain实现动态卸载DLL的代码,

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

using System.Reflection;

namespace UnloadDll

{

class Program

{

static void Main(string[] args)

{

string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName;

Console.WriteLine(callingDomainName);

AppDomain ad = AppDomain.CreateDomain("DLL Unload test");

ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject");

obj.LoadAssembly();

obj.Invoke("TestDll.Class1", "Test", "It's a test");

AppDomain.Unload(ad);

obj = null;

Console.ReadLine();

}

}

class ProxyObject : MarshalByRefObject

{

Assembly assembly = null;

public void LoadAssembly()

{

assembly = Assembly.LoadFile(@"TestDLL.dll");

}

public bool Invoke(string fullClassName, string methodName, params Object[] args)

{

if(assembly == null)

return false;

Type tp = assembly.GetType(fullClassName);

if (tp == null)

return false;

MethodInfo method = tp.GetMethod(methodName);

if (method == null)

return false;

Object obj = Activator.CreateInstance(tp);

method.Invoke(obj, args);

return true;

}

}

}

注意:

1. 要想让一个对象能够穿过AppDomain边界,必须要继承MarshalByRefObject类,否则无法被其他AppDomain使用。

2. 每个线程都有一个默认的AppDomain,可以通过Thread.GetDomain()来得到

AppDomain是CLR的运行单元,它可以加载Assembly、创建对象以及执行程序。

AppDomain是CLR实现代码隔离的基本机制。

每一个AppDomain可以单独运行、停止;每个AppDomain有自己默认的异常处理;

一个AppDomain的运行失败不会影响到其他的AppDomain。

CLR在被CLR Host(windows shell or InternetExplorer or SQL Server)加载后,要创建一个默认的AppDomain,程序的入口点

(Main方法)就是在这个默认的AppDomain中执行。

1.AppDomain vs 进程

AppDomain被创建在进程中,一个进程内可以有多个AppDomain。一个AppDomain只能属于一个进程。

2.AppDomain vs 线程

其实两者本来没什么好对比的。AppDomain是个静态概念,只是限定了对象的边界;线程是个动态概念,它可以运行在不同的

AppDomain。

一个AppDomain内可以创建多个线程,但是不能限定这些线程只能在本AppDomain内执行代码。

CLR中的System.Threading.Thread对象其实是个soft thread,它并不能被操作系统识别;操作系统能识别的是hard thread。

一个soft thread只属于一个AppDomain,穿越AppDomain的是hard thread。当hard thread访问到某个AppDomain时,一个

AppDomain就会为之产生一个soft thread。

hard thread有thread local storage(TLS),这个存储区被CLR用来存储这个hard thread当前对应的AppDomain引用以及soft

thread引用。当一个hard thread穿越到另外一个AppDomain时,TLS中的这些引用也会改变。

当然这个说法很可能是和CLR的实现相关的。

3.AppDomain vs Assembly

Assembly是.Net程序的基本部署单元,它可以为CLR提供用于识别类型的元数据等等。Assembly不能单独执行,它必须被加载到

AppDomain中,然后由AppDomain创建程序集中的对象。

一个Assembly可以被多个AppDomain加载,一个AppDomain可以加载多个Assembly。

每个AppDomain引用到某个类型的时候需要把相应的assembly在各自的AppDomain中初始化。因此,每个AppDomain会单独保持一

个类的静态变量。

4.AppDomain vs 对象

任何对象只能属于一个AppDomain。AppDomain用来隔离对象,不同AppDomain之间的对象必须通过Proxy(reference type)或者

Clone(value type)通信。

引用类型需要继承System.MarshalByRefObject才能被Marshal/UnMarshal(Proxy)。

值类型需要设置Serializable属性才能被Marshal/UnMarshal(Clone)。

5.AppDomain vs Assembly Code

AppDomain和程序集的源代码是什么关系呢?每个程序集的代码会分别装载到各个AppDomain中?

首先我们要把程序集分3类

1.mscorlib,这是每个.net程序都要引用到的程序集。

2.GAC,这个是强命名的公用程序集,可以被所有的.net程序引用。

3.Assembly not in GAC,这是普通的assembly,可以不是强命名,不放到GAC中。

启动CLR,进入entry point时可以设置LoaderOptimization属性:

[LoaderOptimization(LoaderOptimization.MultiDomain]

static void Main()

{...}

LoaderOptimization属性可以设置三个不同的枚举值,来设置针对前面说的三种程序集的代码存放以及访问方式。

LoaderOptimization Enumeration/Attribute

ValueExpected Domains in ProcessEach Domain Expected to Run ...Code for MSCORLIBCode for Assemblies in GACCode for Assemblies not in GAC

SingleDomainOneN/APer-processPer-domainPer-domain

MultiDomainManySame ProgramPer-processPer-processPer-process

MultiDomainHostManyDifferent ProgramsPer-processPer-processPer-domain

1.SingleDomain,由于只启动一个AppDomain,那么code就被直接装载到了AppDomain中,访问静态变量更快捷。

2.MultiDomain,所有的Assembly代码是进程级别的,因此所有的AppDomain只访问一份代码。这大大减少了程序占用的内存,但

是由于程序集的静态变量仍然在各个AppDomain中,因此代码访问静态变量需要先得到AppDomain的引用再进行转换,速度会受到

影响。

3.MultiDomainHost,只有GAC代码是共享的,非GAC的Assembly依然会加载到被使用的AppDomain中,这样提高了静态变量的访问

速度,当然也增加了程序占用的内存。

不管是哪种方式,mscorlib始终是process级别的,即只有一份mscorlib代码在内存中。

C#中动态加载和卸载DLL

在C++中加载和卸载DLL是一件很容易的事,LoadLibrary和FreeLibrary让你能够轻易的在程序中加载DLL,然后在任何地方 卸载。在C#中我们也能使用Assembly.LoadFile实现动态加载DLL,但是当你试图卸载时,你会很惊讶的发现Assembly没有提供任何 卸载的方法。这是由于托管代码的自动垃圾回收机制会做这件事情,所以C#不提供释放资源的函数,一切由垃圾回收来做。

这引发了一个问题,用Assembly加载的DLL可能只在程序结束的时候才会被释放,这也意味着在程序运行期间无法更新被加载的DLL。而这个功能在某 些程序设计时是非常必要的,考虑你正在用反射机制写一个查看DLL中所有函数详细信息的程序,程序提供一个菜单让用户可以选择DLL文件,这时就需要让程 序能够卸载DLL,否则一旦用户重新得到新版本DLL时,必须要重新启动程序,重新选择加载DLL文件,这样的设计是用户无法忍受的。

C#也提供了实现动态卸载DLL的方法,通过AppDomain来实现。AppDomain是一个独立执行应用程序的环境,当AppDomain被卸载的 时候,在该环境中的所有资源也将被回收。关于AppDomain的详细资料参考MSDN。下面是使用AppDomain实现动态卸载DLL的代码,

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

using System.Reflection;

namespace UnloadDll

{

class Program

{

static void Main(string[] args)

{

string callingDomainName = AppDomain.CurrentDomain.FriendlyName;//Thread.GetDomain().FriendlyName;

Console.WriteLine(callingDomainName);

AppDomain ad = AppDomain.CreateDomain("DLL Unload test");

ProxyObject obj = (ProxyObject)ad.CreateInstanceFromAndUnwrap(@"UnloadDll.exe", "UnloadDll.ProxyObject");

obj.LoadAssembly();

obj.Invoke("TestDll.Class1", "Test", "It's a test");

AppDomain.Unload(ad);

obj = null;

Console.ReadLine();

}

}

class ProxyObject : MarshalByRefObject

{

Assembly assembly = null;

public void LoadAssembly()

{

assembly = Assembly.LoadFile(@"TestDLL.dll");

}

public bool Invoke(string fullClassName, string methodName, params Object[] args)

{

if(assembly == null)

return false;

Type tp = assembly.GetType(fullClassName);

if (tp == null)

return false;

MethodInfo method = tp.GetMethod(methodName);

if (method == null)

return false;

Object obj = Activator.CreateInstance(tp);

method.Invoke(obj, args);

return true;

}

}

}

注意:

1. 要想让一个对象能够穿过AppDomain边界,必须要继承MarshalByRefObject类,否则无法被其他AppDomain使用。

2. 每个线程都有一个默认的AppDomain,可以通过Thread.GetDomain()来得到


相关内容

  • net-framework微软认证考试题库
  • 你正在开发一个自定义事件处理去自动打印所有打开的文档.事件处理可以指定要打印的份 数.为此,你需要开发一个传递给事件处理程序的自定义事件参数类,你应该使用下面那个 代码段? A. public class PrintingArgs { private int copies; public Print ...

  • 学生信息管理
  • 天津职业技术师范大学 Tianjin University of Technology and Education <数据库系统概论> 课程设计报告 学生信息管理系统 专 业: 计算机科学与技术 班级学号: 学生姓名: 指导教师: 二〇一一年六月 一.实验名称: 学生信息管理系统 张彩娟 ...

  • 总结4:有关简述的问题
  • 总结4:有关简述的问题 1.描述一下C#中索引器的实现过程,是否只能根据数字进行索引? 索引器允许类或结构的实例就像数组一样进行索引. 索引器类似于属性,不同之处在于它们的访问器采用参数. class SampleCollection { private T[] arr = new T[100]; ...

  • 多线程与多进程的区别
  • 进程: 子进程是父进程的复制品.子进程获得父进程数据空间.堆和栈的复制品. 线程:相对与进程而言,线程是一个更接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列. 根本区别:用多进程每个进程有自己的地址空间,线程则共享地址空间.所以其他区别都是由此而来的: ...

  • 计算机基础A考试题
  • 1 计算机新技术 新技术有嵌入式计算机 网格计算和中间件技术 嵌入式计算机是作为一个信息处理部件,嵌入到应用系统之中的计算机. 网格计算是利用因特网把分散在不同地理位置的计算机组成一个虚拟的超级计算机每一台参与计算的计算机是一个结点整个计算由千万个结点组成一张网格.这种计算叫网格计算. 中间件技术是 ...

  • 1.NET知识结构框架
  • .NET 知识结构框架 .NET 介绍微软.NET 战略及技术体系,.NET Framework 框架类库(FCL ),公共语言运行时(CLR ),通用类型系统(CTS ),公共语言规范(CLS ),程序集(Assembly ),应用程序域(Application Domain ).了解微软.net ...

  • 进程和线程的区别是什么
  • 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性.进程和线程的区别在于: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大 ...

  • 同步和互斥的区别
  • 信号量 互斥量 条件变量 同步和互斥 一.信号量: 用于进程间通信(linux 中用于线程) 独立于进程 在两个进程中都要初始化,若一个已创建,则另一个获取 可以执行多次v 操作,然后再执行p 操作 信号量的一些概念: 以下是信号灯(量) 的一些概念: 信号灯与互斥锁和条件变量的主要不同在于&quo ...

  • 2013阿里巴巴笔试题
  • 一.单项选择题 1.下列说法不正确的是:(B) A.SATA硬盘的速度速度大约为500Mbps/s B.读取18XDVD光盘数据的速度为1Gbps C.前兆以太网的数据读取速度为1Gpbs D.读取DDR3内存数据的速度为100Gbps 解析:有说B的,有说D的,B肯定是不对的吧. 关于B选项: 理 ...