Remoting学习笔记之一
应用程序域
我们知道所有的.Net 应用程序都运行在托管环境中,但操作系统只提供进程供程序运行,而进程只是提供了基本的内存管理,它不了解什么是托管代码。所以托管代码,也可以说是我们创建的.Net程序,是无法直接运行在操作系统进程中的。为了使托管代码能够运行在非托管的进程之上,就需要有一个中介者,这个中介者可以运行于非托管的进程之上,同时向托管代码提供运行的环境。这个中介者就是 应用程序域(Application Domain,简写为App Domain)。所以我们的.Net程序,不管是Windows窗体、Web窗体、控制台应用程序,又或者是一个程序集,总是运行在一个App Domain中。
如果只有一个类库程序集(.dll文件),是无法启动一个进程的(它并非可执行文件)。所以,创建进程需要加载一个可执行程序集(Windows 窗体、控制台应用程序等.exe文件)。当可执行程序集加载完毕,.Net会在当前进程中创建一个新的应用程序域,称为 默认应用程序域。一个进程中只会创建一个默认应用程序域,这个应用程序域的名称与程序集名称相同。默认应用程序域不能被卸载,并且与其所在的进程同生共灭。
那么应用程序域是如何提供托管环境的呢?简单来说,应用程序域只是允许它所加载的程序集访问由.Net Runtime所提供的服务。这些服务包括托管堆,垃圾回收器(,JIT 编译器等.Net底层机制,这些服务本身(它们构成了.Net Runtime)是由非托管C++实现的。
在一个进程中可以包含多个应用程序域,一个应用程序域中可以包含多个程序集。比如说,我们的Asp.Net应用程序都运行在aspnet_wp.exe(IIS5.0)或者w3wp.exe(IIS6.0)进程中,而IIS下通常会创建多个站点,那么是为每个站点都创建一个独立的进程么?不是的,而是为每个站点创建其专属的应用程序域,而这些应用程序域运行在同一个进程(w3wp.exe或aspnet_wp.exe)中。这样做起码有两个好处:1、在一个进程中创建多个App Domain要比创建和运行多个进程需要少得多系统开销;2、实现了错误隔离,一个站点如果出现了致命错误导致崩溃,只会影响其所在的应用程序域,而不会影响到其他站点所在的应用程序域
相关操作的例子
获取当前代码运行的应用程序域
AppDomain currentDomain = AppDomain.CurrentDomain;
获取应用程序域的名称
string name=AppDomain.CurrentDomain.FriendlyName;
创建新的应用程序域
AppDomain newDomain = AppDomain.CreateDomain("New Domain");
在应用程序域中创建对象
Demo demo = (Demo)newDomain.CreateInstanceAndUnwrap("MyRemoting", "MyRemoting.Demo");
下面我们来看一个具体的例子
先创建一个类
public class Demo { public Demo() { Console.WriteLine("------------Demo-----------"); } public void ShowAppDomain() { AppDomain currentDomain = AppDomain.CurrentDomain; Console.WriteLine(currentDomain.FriendlyName); } }
然后在默认的控制台程序中调用它
class Program { static void Main(string[] args) { Test1(); } // 在当前AppDomain中创建一个对象 static void Test1() { AppDomain currentDomain = AppDomain.CurrentDomain; // 获取当前应用程序域 Console.WriteLine(currentDomain.FriendlyName); // 打印名称 Demo obj; // obj = new Demo() // 常规的创建对象的方式 // 在默认应用程序域中创建对象 obj = (Demo)currentDomain.CreateInstanceAndUnwrap("MyRemoting", "MyRemoting.Demo"); obj.ShowAppDomain(); } }
结果如下:
下面我们将程序稍微做一点修改,再来看看
static void Test2() {
AppDomain currentDomain = AppDomain.CurrentDomain;
Console.WriteLine(currentDomain.FriendlyName);
// 创建一个新的应用程序域 - NewDomain
AppDomain newDomain = AppDomain.CreateDomain("NewDomain");
Demo obj;
// 在新的应用程序域中创建对象
obj = (Demo)newDomain.CreateInstanceAndUnwrap("MyRemoting", "MyRemoting.Demo");
obj.ShowAppDomain();
}
此时,程序运行的时候就会抛出一个异常“类型MyRemoting.Demo未标记为可序列化”。这个异常是怎么造成的呢,Demo obj,这说明了obj是在当前的默认应用程序域,也就是MyRemoting.exe中声明的;然后我们在往下看,类型的实例(对象本身)却是通过 newDomain.CreateInstanceAndUnwrap() 在新创建的应用程序域 -- NewDomain中创建的。这样就造成这样一个情况,对象的引用在默认的应用程序域中,而对象的实例确在新建的应用程序域中,在默认情况下应用程序域直接是隔离的,不能直接访问,所以就引发了这个异常。怎么解决呢,我们有以下两种方法来解决:
一种是给Demo加上属性:
[Serializable] public class Demo
一种是让Demo继承MarshalByRefObject
public class Demo : MarshalByRefObject