yang

危机感,每天进步一点

导航

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

 

 

 


posted on 2012-01-07 11:34  jiayang44  阅读(276)  评论(0)    收藏  举报