一、深入了解.Net上下文
3.1 .NET上下文的概念
应用程序域是进程中承载程序集的路基分区,在应用程序域当中,存在更细粒度的用于承载.Net对象的实体,用于承载.Net实体的东西就叫.Net上下文Context。
所有的.Net对象都存在于上下文当中,每个AppDomain当中至少存在一个默认上下文(context 0)。
一般不需要指定特定上下文的对象被称为上下文灵活对象(context-agile),建立此对象不需要特定的操作,只需要由CLR自行管理,一般这些对象都会被建立在默认上下文当中。
图3.0
3.2 透明代理
在上下文的接口当中存在着一个消息接收器负责检测拦截和处理信息,当对象是MarshalByRefObject的子类的时候,CLR将会建立透明代理,实现对象与消息之间的转换。
应用程序域是CLR中资源的边界,一般情况下,应用程序域中的对象不能被外界的对象所访问。而MarshalByRefObject 的功能就是允许在支持远程处理的应用程序中跨应用程序域边界访问对象,在使用.NET Remoting远程对象开发时经常使用到的一个父类。
此文章针对的是进程与应用程序域的作用,关于MarshalByRefObject的使用已经超越了本文的范围,关于.NET Remoting 远程对象开发可参考:“回顾.NET Remoting分布式开发”。
3.3 上下文绑定
当系统需要对象使用消息接收器机制的时候,即可使用ContextBoundObject类。ContextBoundObject继承了MarshalByRefObject类,保证了它的子类都会通过透明代理被访问。
在第一节介绍过:一般类所建立的对象为上下文灵活对象(context-agile),它们都由CLR自动管理,可存在于任意的上下文当中。而 ContextBoundObject 的子类所建立的对象只能在建立它的对应上下文中正常运行,此状态被称为上下文绑定。其他对象想要访问ContextBoundObject 的子类对象时,都只能通过代透明理来操作。
下面的例子,是上下文绑定对象与上下文灵活对象的一个对比。Example 是一个普通类,它的对象会运行在默认上下文当中。而ContextBound类继承了ContextBoundObject,它的对象是一个上下文绑定对象。ContextBound还有一个Synchronization特性,此特性会保证ContextBound对象被加载到一个线程安全的上下文当中运行。另外,Context类存在ContextProperties属性,通过此属性可以获取该上下文的已有信息。
二、进程、应用程序域、线程的相互关系
4.1跨AppDomain运行代码
在应用程序域之间的数据时相互独立的,当需要在其他AppDomain当中执行当前AppDomain中的程序集代码时,可以使用CrossAppDomainDelegate委托。把CrocessAppDomainDelegate委托绑定方法以后,通过AppDomain的DoCallBack方法即可执行委托。
class Program { static void Main(string[] args) { Console.WriteLine("主程序域开始运行"); //建立新的应用程序域对象 AppDomain newAppDomain = AppDomain.CreateDomain("子程序域"); //绑定CrossAppDomainDelegate的委托方法 CrossAppDomainDelegate crossAppDomainDelegate = new CrossAppDomainDelegate(MyCallBack); //子应用程序域调用委托,相当于子程序域调用主程序域里的方法 newAppDomain.DoCallBack(crossAppDomainDelegate); //绑定程序域卸载事件处理方法 newAppDomain.DomainUnload += (obj, e) => { Console.WriteLine("子程序域卸载!"); }; //卸载子程序域 AppDomain.Unload(newAppDomain); Console.ReadKey(); } static public void MyCallBack() { string name = AppDomain.CurrentDomain.FriendlyName; for (int n = 0; n < 4; n++) { Console.WriteLine(string.Format("当前程序运行于{0}",name)); } } }
输出结果如下:
4.2线程是跨AppDomain的
线程存在于进程当中,它在不同的时刻可以运行于多个不同的AppDomain当中。它是进程中的基本执行单元 ,在进程入口执行的第一个线程被视为这个进程的主线程。在.Net应用程序中,都是以Main()方法作为入口的,当调用此方法时,系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。
首先搞一个控制台程序,生成后放到D盘,代码如下:
class Program { static void Main(string[] args) { var message = string.Format("当前线程Id 是:{0}\t 当前应用程序域是:{1}", Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id); Console.WriteLine(message); Console.ReadKey(); } }
新建另外一个项目:
class Program { static void Main(string[] args) { //当前应用程序域信息 Console.WriteLine("主程序与开始运行!"); ShowMessage(); //建立新的应用程序域对象 AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain"); //在新的应用程序域中执行Example.exe newAppDomain.ExecuteAssembly(@"D:\ConsoleApplication1.exe"); AppDomain.Unload(newAppDomain); Console.ReadKey(); } public static void ShowMessage() { var message = string.Format("主线程Id是:{0}\t 主应用程序域是:{1}", Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id); Console.WriteLine(message); } }
输出结果如下:
总结:
进程(Process)、线程(Thread)、应用程序域(AppDomain)、上下文(Context)的关系如图5.0,一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程只会处于一个应用程序域内。线程也能穿梭于多个上下文当中,进行对象的调用。
虽然进程、应用程序域与上下文在平常的开发中并非经常用到,但深入地了解三者的关系,熟悉其操作方式对合理利用系统的资源,提高系统的效率是非常有意义的。